• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

APN-Pucky / tyrant_optimize / 15799373702

21 Jun 2025 08:29PM UTC coverage: 70.309% (-0.1%) from 70.425%
15799373702

push

github

web-flow
ci: lcov ignore (#84)

* ci: lcov ignore

* latest ubutnut

* ci: lcov more ignore

* try less than latest

* cmake toolchain for windows

* ubuntu in ubuntu

* Revert "cmake toolchain for windows"

This reverts commit 881abfcd5.

* install git

* fetch depth

* add sudo

* non interactive debian

* set time zone

* ok

* 2

* add_pip

* no source but sh

4755 of 6763 relevant lines covered (70.31%)

9581795.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.18
/tyrant_optimize.cpp
1
// This program is free software: you can redistribute it and/or modify
2
// it under the terms of the GNU General Public License as published by
3
// the Free Software Foundation, either version 3 of the License, or
4
// (at your option) any later version.
5

6
// This program is distributed in the hope that it will be useful,
7
// but WITHOUT ANY WARRANTY; without even the implied warranty of
8
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9
// GNU General Public License for more details.
10

11
// You should have received a copy of the GNU General Public License
12
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
13
//------------------------------------------------------------------------------
14
// #define NDEBUG
15
#define BOOST_THREAD_USE_LIB
16

17
#ifndef _WIN32
18
#include <unistd.h>
19
#else
20
#include <io.h>
21
#define F_OK 0
22
#define access(f, m) _access((f), (m))
23
#endif
24
#include <array>
25
#include <cassert>
26
#include <chrono>
27
#include <cstring>
28
#include <ctime>
29
#include <functional>
30
#include <iostream>
31
#include <map>
32
#include <unordered_map>
33
#include <set>
34
#include <stack>
35
#include <fstream>
36
#include <string>
37
#include <iomanip>
38
#include <tuple>
39
#include <vector>
40
#include <math.h>
41
#include <boost/algorithm/string.hpp>
42
#include <boost/filesystem.hpp>
43
#include <boost/lexical_cast.hpp>
44
#include <boost/math/distributions/binomial.hpp>
45
#include <boost/optional.hpp>
46
#include <boost/range/join.hpp>
47
#include <boost/thread/barrier.hpp>
48
#include <boost/thread/mutex.hpp>
49
#include <boost/thread/thread.hpp>
50
#include <boost/timer/timer.hpp>
51
#include <boost/tokenizer.hpp>
52
#include "card.h"
53
#include "cards.h"
54
#include "deck.h"
55
#include "read.h"
56
#include "sim.h"
57
#include "tyrant.h"
58
#include "xml.h"
59

60
#include "hPMML.h"
61

62
#define DEFINE_GLOBALS
63
#include "algorithms.h"
64

65
// OpenMP Header
66
#ifdef _OPENMP
67
#include <omp.h>
68
#endif
69

70
// Android Headers
71
#if defined(ANDROID) || defined(__ANDROID__)
72
#include <jni.h>
73
#include <android/log.h>
74
#endif
75

76
using namespace tuo;
77
using namespace proc;
78
// init
79
/*
80
namespace tuo {
81
  unsigned opt_num_threads=4;
82
    gamemode_t gamemode{fight};
83
    OptimizationMode optimization_mode{OptimizationMode::notset};
84
    std::unordered_map<unsigned, unsigned> owned_cards;
85
    const Card* owned_alpha_dominion{nullptr};
86
    bool use_owned_cards{true};
87
    bool opt_skip_unclaimed_decks{false};
88
    //bool opt_trim_unclaimed_decks{false};
89
    unsigned min_deck_len{1};
90
    unsigned max_deck_len{10};
91
    unsigned opt_freezed_cards{0};
92
    unsigned freezed_cards{0};
93
    unsigned fund{0};
94
    long double target_score{100};
95
    long double min_increment_of_score{0};
96
    long double confidence_level{0.99};
97
    bool use_top_level_card{true};
98
    bool use_top_level_commander{true};
99
    bool mode_open_the_deck{false};
100
    bool use_owned_dominions{true};
101
    bool use_maxed_dominions{false};
102
    unsigned use_fused_card_level{0};
103
    unsigned use_fused_commander_level{0};
104
    bool print_upgraded{false};
105
    bool print_values{false};
106
    bool simplify_output{false};
107
    bool show_ci{false};
108
    bool use_harmonic_mean{false};
109
    unsigned iterations_multiplier{10};
110
    unsigned sim_seed{0};
111
    unsigned flexible_iter{20};
112
    unsigned flexible_turn{10};
113
    Requirement requirement;
114
#ifndef NQUEST
115
    Quest quest;
116
#endif
117
    std::unordered_set<unsigned> allowed_candidates;
118
    std::unordered_set<unsigned> disallowed_candidates;
119
    std::chrono::time_point<std::chrono::system_clock> start_time;
120
    long double maximum_time{0};
121
    //anneal
122
    long double temperature = 1000;
123
    long double coolingRate = 0.001;
124
    //genetic
125
    unsigned generations = 50;
126
    unsigned pool_size = 0;
127
    unsigned min_pool_size = 20;
128
    double opt_pool_keep = 1;
129
    double opt_pool_mutate = 1;
130
    double opt_pool_cross = 1;
131
    //fort_climb
132
    unsigned yfpool{0};
133
    unsigned efpool{0};
134
    std::vector<Faction> factions;
135
    bool invert_factions{false};
136
    bool only_recent{false};
137
    bool prefered_recent{false};
138
    unsigned recent_percent{5};
139
    std::vector<Skill::Skill> skills;
140
    bool invert_skills{false};
141
    std::vector<Skill::Skill> prefered_skills;
142
    unsigned prefered_factor{3};
143

144
#if defined(ANDROID) || defined(__ANDROID__)
145
    JNIEnv *envv;
146
    jobject objv;
147
#endif
148
}
149
*/
150

151
static const unsigned int crc32_table[] =
152
    {
153
        0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
154
        0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
155
        0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
156
        0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
157
        0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
158
        0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
159
        0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
160
        0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
161
        0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
162
        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
163
        0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
164
        0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
165
        0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
166
        0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
167
        0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
168
        0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
169
        0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
170
        0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
171
        0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
172
        0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
173
        0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
174
        0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
175
        0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
176
        0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
177
        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
178
        0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
179
        0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
180
        0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
181
        0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
182
        0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
183
        0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
184
        0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
185
        0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
186
        0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
187
        0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
188
        0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
189
        0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
190
        0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
191
        0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
192
        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
193
        0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
194
        0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
195
        0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
196
        0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
197
        0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
198
        0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
199
        0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
200
        0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
201
        0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
202
        0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
203
        0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
204
        0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
205
        0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
206
        0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
207
        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
208
        0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
209
        0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
210
        0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
211
        0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
212
        0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
213
        0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
214
        0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
215
        0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
216
        0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
217

218
unsigned int
219
xcrc32(const unsigned char *buf, int len, unsigned int init)
54,116,282✔
220
{
221
    unsigned int crc = init;
54,116,282✔
222
    while (len--)
1,267,776,554✔
223
    {
224
        crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *buf) & 255];
1,213,660,272✔
225
        buf++;
1,213,660,272✔
226
    }
227
    return crc;
×
228
}
229

230
unsigned int file_xrcrc32(std::string filename, unsigned int init)
2,541✔
231
{
232
    std::ifstream file;
2,541✔
233
    file.open(filename);
2,541✔
234
    std::string line;
2,541✔
235
    unsigned int crc = init;
2,541✔
236
    while (getline(file, line))
54,121,364✔
237
    {
238
        crc = xcrc32((const unsigned char *)line.c_str(), line.length(), crc);
108,235,105✔
239
    }
240
    return crc;
5,082✔
241
}
2,541✔
242

243
unsigned int checksumcards(std::string prefix)
121✔
244
{
245
    unsigned int crc = 0;
121✔
246
    // loop cardsections
247
    unsigned int ii = 1;
121✔
248

249
    crc = file_xrcrc32(prefix + "data/raids.xml", crc);
121✔
250
    crc = file_xrcrc32(prefix + "data/missions.xml", crc);
121✔
251
    std::string fname = prefix + "data/cards_section_1.xml";
121✔
252
    while (access(fname.c_str(), F_OK) == 0)
2,420✔
253
    {
254
        crc = file_xrcrc32(fname, crc);
4,598✔
255
        ii += 1;
2,299✔
256
        fname = prefix + "data/cards_section_" + std::to_string(ii) + ".xml";
4,598✔
257
    }
258
    return crc;
121✔
259
}
121✔
260

261
void load_ml(std::string prefix)
78✔
262
{
263
    if (use_ml)
78✔
264
    {
265
        win_model = hpmml::Model(prefix + "data/win.pmml");
2✔
266
        stall_model = hpmml::Model(prefix + "data/stall.pmml");
2✔
267
        loss_model = hpmml::Model(prefix + "data/loss.pmml");
2✔
268
        points_model = hpmml::Model(prefix + "data/points.pmml");
2✔
269
    }
270
}
78✔
271

272
// load database map from file
273
void load_db(std::string prefix)
78✔
274
{
275
    if (use_db_load)
78✔
276
    {
277
        _DEBUG_MSG(1, "start loading db\n");
60✔
278
        // open file to read from
279
        std::ifstream file;
60✔
280
        file.open(prefix + "data/database.yml");
60✔
281
        // read map from file
282
        std::string name;
60✔
283
        uint64_t wins, draws, losses, points, count;
60✔
284
        std::string version, check;
60✔
285
        std::string line;
60✔
286
        getline(file, line);
60✔
287
        if (line.find(":") != std::string::npos)
60✔
288
            version = line.substr(line.find(":") + 2);
59✔
289
        std::cout << "DB version " << version << std::endl;
60✔
290
        getline(file, line);
60✔
291
        if (line.find(":") != std::string::npos)
60✔
292
            check = line.substr(line.find(":") + 2);
59✔
293
        std::cout << "DB checksum " << check << std::endl;
60✔
294

295
        if (use_strict_db)
60✔
296
        {
297
            if (version.compare(TYRANT_OPTIMIZER_VERSION) != 0)
60✔
298
            {
299
                std::cout << "DB TUO version outdated" << std::endl;
1✔
300
                std::cout << "DB TUO version " << version << " != " << TYRANT_OPTIMIZER_VERSION << std::endl;
1✔
301
                std::cout << "DB ignoring database.yml" << std::endl;
1✔
302
                use_db_load = false;
1✔
303
                return;
1✔
304
            }
305
            if (check.compare(std::to_string(checksumcards(prefix))) != 0)
118✔
306
            {
307
                std::cout << "DB cards_sections mismatch to db" << std::endl;
×
308
                std::cout << "DB cards_sections checksum " << checksumcards(prefix) << " != " << check << std::endl;
×
309
                std::cout << "DB ignoring database.yml" << std::endl;
×
310
                use_db_load = false;
×
311
                return;
×
312
            }
313
        }
314

315
        std::string hfield;
59✔
316
        std::string hydeck;
59✔
317
        std::string hedeck;
59✔
318
        while (getline(file, line))
46,625✔
319
        {
320
            if (line.rfind("\t\t", 0) == 0 && line.find(":") != std::string::npos)
86,729✔
321
            {
322
                hedeck = line.substr(2, line.find(":") - 2);
40,222✔
323
                std::istringstream reader(line.substr(line.find(":") + 2));
40,222✔
324
                reader >> wins >> draws >> losses >> points >> count;
40,222✔
325
                database[hfield][hydeck][hedeck] = {wins, draws, losses, points, count};
40,222✔
326
                // std::cout << "load db: " << hfield << " " << hydeck << " " << hedeck << " " <<wins<< " "<< draws<< " "<< losses<< " " <<points<< " " <<count << std::endl;
327
            }
40,222✔
328
            else if (line.rfind("\t", 0) == 0 && line.find(":") != std::string::npos)
11,976✔
329
            {
330
                hydeck = line.substr(1, line.find(":") - 1);
5,691✔
331
            }
332
            else if (line.find(":") != std::string::npos)
594✔
333
            {
334
                hfield = line.substr(0, line.find(":"));
594✔
335
            }
336
            else
337
            {
338
                std::cout << "unknown db line: " << line;
46,566✔
339
            }
340
        }
341
        // close file
342
        file.close();
59✔
343
        _DEBUG_MSG(1, "done loading db\n");
59✔
344
    }
60✔
345
}
346

347
// write database map to file
348
void write_db(std::string prefix)
78✔
349
{
350
    if (use_db_write)
78✔
351
    {
352
        _DEBUG_MSG(1, "start writing to db\n");
62✔
353
        // open file to write to
354
        std::ofstream file;
62✔
355
        file.open(prefix + "data/database.yml");
62✔
356
        file << "version: " << TYRANT_OPTIMIZER_VERSION << std::endl;
62✔
357

358
        file << "xml_check_sum: " << checksumcards(prefix) << std::endl;
124✔
359
        auto lines_to_write = db_limit;
62✔
360
        // write map to file
361
        for (auto it1 = database.begin(); lines_to_write != 0 && it1 != database.end(); ++it1)
689✔
362
        {
363
            file << it1->first << ":" << std::endl;
627✔
364
            for (auto it2 = it1->second.begin(); lines_to_write != 0 && it2 != it1->second.end(); ++it2)
6,583✔
365
            {
366
                file << "\t" << it2->first << ":" << std::endl;
5,956✔
367
                for (auto it3 = it2->second.begin(); lines_to_write != 0 && it3 != it2->second.end(); ++it3)
48,072✔
368
                {
369
                    file << "\t\t" << it3->first << ": " << it3->second.wins << " " << it3->second.draws << " " << it3->second.losses << " " << it3->second.points << " " << it3->second.count << std::endl;
42,116✔
370
                    if (lines_to_write > 0)
42,116✔
371
                        lines_to_write--;
×
372
                }
373
            }
374
        }
375
        // close file
376
        file.close();
62✔
377
        _DEBUG_MSG(1, "done writing to db\n");
62✔
378
    }
62✔
379
}
78✔
380

381
void init()
79✔
382
{
383
    debug_str.clear();
79✔
384
    database.clear();
79✔
385
    thread_num_iterations = 0; // written by threads
79✔
386
    thread_results = nullptr;  // written by threads
79✔
387
    thread_best_results = nullptr;
79✔
388
    thread_compare = false;
79✔
389
    thread_compare_stop = false; // written by threads
79✔
390
    destroy_threads;
79✔
391
    opt_num_threads = 4;
79✔
392
    gamemode = fight;
79✔
393
    optimization_mode = OptimizationMode::notset;
79✔
394
    owned_cards.clear();
79✔
395
    owned_alpha_dominion = nullptr;
79✔
396
    use_owned_cards = true;
79✔
397
    opt_skip_unclaimed_decks = false;
79✔
398
    // opt_trim_unclaimed_decks=false;
399
    min_deck_len = 1;
79✔
400
    max_deck_len = 10;
79✔
401
    opt_freezed_cards = 0;
79✔
402
    freezed_cards = 0;
79✔
403
    fund = 0;
79✔
404
    target_score = 100;
79✔
405
    min_increment_of_score = 0;
79✔
406
    confidence_level = 0.99;
79✔
407
    use_top_level_card = true;
79✔
408
    use_top_level_commander = true;
79✔
409
    mode_open_the_deck = false;
79✔
410
    use_owned_dominions = true;
79✔
411
    use_maxed_dominions = false;
79✔
412
    use_fused_card_level = 0;
79✔
413
    use_fused_commander_level = 0;
79✔
414
    print_upgraded = false;
79✔
415
    print_values = false;
79✔
416
    vc_x = 0;
79✔
417
    simplify_output = false;
79✔
418
    show_ci = false;
79✔
419
    use_harmonic_mean = false;
79✔
420
    iterations_multiplier = 10;
79✔
421
    sim_seed = 0;
79✔
422
    flexible_iter = 20;
79✔
423
    flexible_turn = 20;
79✔
424
    eval_iter = 8;
79✔
425
    eval_turn = 8;
79✔
426
    requirement.num_cards.clear();
79✔
427
    for (unsigned i(0); i < Skill::num_skills; ++i)
4,503✔
428
    {
429
        auto s = static_cast<Skill::Skill>(i);
4,424✔
430
        x_skill_scale[s] = 1.0;
4,424✔
431
        n_skill_scale[s] = 1.0;
4,424✔
432
        c_skill_scale[s] = 1.0;
4,424✔
433
    }
434
    hp_scale = 1.0;
79✔
435
    atk_scale = 1.0;
79✔
436
#ifndef NQUEST
437
    // quest = new Quest(); //TODO Quest bugged in Android now here
438
#endif
439
    allowed_candidates.clear();
79✔
440
    disallowed_candidates.clear();
79✔
441
    disallowed_recipes.clear();
79✔
442

443
    // std::chrono::time_point<std::chrono::system_clock> start_time;
444
    maximum_time = 0;
79✔
445
    temperature = 1000;
79✔
446
    coolingRate = 0.001;
79✔
447

448
    generations = 50;
79✔
449
    pool_size = 0;
79✔
450
    min_pool_size = 20;
79✔
451
    opt_pool_keep = 1;
79✔
452
    opt_pool_mutate = 1;
79✔
453
    opt_pool_cross = 1;
79✔
454

455
    min_beam_size = 5;
79✔
456

457
    yfpool = 0;
79✔
458
    efpool = 0;
79✔
459

460
    factions.clear();
79✔
461
    invert_factions = false;
79✔
462
    only_recent = false;
79✔
463
    prefered_recent = false;
79✔
464
    recent_percent = 5;
79✔
465
    skills.clear();
79✔
466
    invert_skills = false;
79✔
467
    prefered_skills.clear();
79✔
468
    prefered_factor = 3;
79✔
469

470
    all_cards.clear();
79✔
471

472
    // fix defaults
473
    for (int i = 0; i < Fix::num_fixes; ++i)
1,027✔
474
        fixes[i] = false;
948✔
475

476
    // recommended/default fixes
477
    fixes[Fix::enhance_early] = true;
79✔
478
    fixes[Fix::revenge_on_death] = true;
79✔
479
    fixes[Fix::death_from_bge] = true;
79✔
480
    fixes[Fix::legion_under_mega] = true;
79✔
481
    // Sort of a misnomer, but updated skills are also treated as fixes
482
        fixes[Fix::barrier_each_turn] = true;
79✔
483
        fixes[Fix::dont_evade_mimic_selection] = true;
79✔
484
        fixes[Fix::leech_increase_max_hp] = true;
79✔
485
        fixes[Fix::subdue_before_attack] = true;
79✔
486
        fixes[Fix::counter_without_damage] = true;
79✔
487
        fixes[Fix::poison_after_attacked] = true;
79✔
488

489
        fixes[Fix::corrosive_protect_armor] = true;
79✔
490
  
491
  
492
    db_limit = -1;
79✔
493
    use_strict_db = true;
79✔
494
    use_db_write = true;
79✔
495
    use_db_load = true;
79✔
496

497
    use_ml = false;
79✔
498
    use_only_ml = false;
79✔
499
    ml_precision = 0.01;
79✔
500
}
79✔
501

502
#if defined(ANDROID) || defined(__ANDROID__)
503
extern "C" JNIEXPORT void
504

505
    JNICALL
506
    Java_de_neuwirthinformatik_alexander_mtuo_TUO_callMain(
507
        JNIEnv *env,
508
        jobject obj /* this */, jobjectArray stringArray)
509
{
510
    envv = env;
511
    objv = obj;
512
    // from: https://stackoverflow.com/questions/8870174/is-stdcout-usable-in-android-ndk and https://gist.github.com/dzhioev/6127982
513
    class androidbuf : public std::streambuf
514
    {
515
    public:
516
        enum
517
        {
518
            bufsize = 512
519
        }; // ... or some other suitable buffer size
520
        androidbuf() { this->setp(buffer, buffer + bufsize - 1); }
521

522
    private:
523
        int overflow(int c)
524
        {
525
            if (c == traits_type::eof())
526
            {
527
                *this->pptr() = traits_type::to_char_type(c);
528
                this->sbumpc();
529
            }
530
            sync_me(c);
531
            return traits_type::not_eof(c);
532
        }
533

534
        void sync_me(int overflow=0) {
535
            if (this->pbase() != this->pptr())
536
            {
537
                char writebuf[bufsize+2];
538
                memcpy(writebuf, this->pbase(), this->pptr() - this->pbase() );
539
                writebuf[this->pptr() - this->pbase() ] = overflow;
540
                writebuf[this->pptr() - this->pbase() + 1 ] = '\0';
541
                //auto sss = std::string(writebuf).c_str();
542
                ///*
543
                __android_log_print(ANDROID_LOG_DEBUG,
544
                                    "TUO_TUO",
545
                                    "%s",
546
                                    writebuf);
547
                //*/
548
                jstring jstr = envv->NewStringUTF(writebuf);
549
                jclass clazz = envv->FindClass("de/neuwirthinformatik/alexander/mtuo/TUO");
550
                jmethodID messageMe = envv->GetMethodID(clazz, "output", "(Ljava/lang/String;)V");
551
                envv->CallVoidMethod(objv, messageMe, jstr);
552
                this->setp(buffer, buffer + bufsize - 1);
553
            }
554
        }
555

556
        int sync()
557
        {
558
            sync_me(0);
559
            return 0;
560
        }
561
        char buffer[bufsize];
562
    };
563
    auto ao = new androidbuf();
564
    auto ae = new androidbuf();
565
    std::cout.rdbuf(ao);
566
    std::cerr.rdbuf(ae);
567
    __android_log_write(ANDROID_LOG_DEBUG, "TUO_TUO", "START");
568
    int stringCount = env->GetArrayLength(stringArray);
569
    char **param = new char *[stringCount];
570
    const char **cparam = new const char *[stringCount];
571
    jstring *strs = new jstring[stringCount];
572
    for (int i = 0; i < stringCount; i++)
573
    {
574
        strs[i] = (jstring)(*env).GetObjectArrayElement(stringArray, i);
575
        cparam[i] = ((*env).GetStringUTFChars(strs[i], NULL));
576
        param[i] = const_cast<char *>(cparam[i]);
577
    }
578

579
    main(stringCount, cparam);
580
    std::cout << std::flush;
581
    __android_log_write(ANDROID_LOG_DEBUG, "TUO_TUO", "END");
582

583
    for (int i = 0; i < stringCount; i++)
584
    {
585
        env->ReleaseStringUTFChars(strs[i], cparam[i]);
586
    }
587
    // std::string text = "return";
588
    // return env->NewStringUTF(text.c_str());
589
    delete[] param;
590
    delete[] cparam;
591
    delete[] strs;
592
    delete ao;
593
    delete ae;
594
}
595

596
extern "C" JNIEXPORT jstring
597

598
    JNICALL
599
    Java_de_neuwirthinformatik_alexander_mtuo_TUO_stringFromJNI(JNIEnv *env,
600
                                                                jobject thiz, jstring s)
601
{
602
    std::string str = env->GetStringUTFChars(s, NULL);
603
    str += "hello.txt";
604
    __android_log_write(ANDROID_LOG_DEBUG, "TUO_TUO", str.c_str());
605
    FILE *file = fopen(str.c_str(), "w+");
606

607
    if (file != NULL)
608
    {
609
        fputs("HELLO WORLD!\n", file);
610
        fflush(file);
611
        fclose(file);
612
    }
613

614
    return env->NewStringUTF("Hello from JNI (with file io)!");
615
}
616
#endif
617
using namespace std::placeholders;
618

619
//------------------------------------------------------------------------------
620

621
//------------------------------------------------------------------------------
622
Deck *find_deck(Decks &decks, const Cards &all_cards, std::string deck_name)
594✔
623
{
624
    Deck *deck = decks.find_deck_by_name(deck_name);
594✔
625
    if (deck != nullptr)
594✔
626
    {
627
        deck->resolve();
538✔
628
        return (deck);
538✔
629
    }
630
    decks.decks.emplace_back(Deck{all_cards});
112✔
631
    deck = &decks.decks.back();
56✔
632
    deck->set(deck_name);
56✔
633
    deck->resolve();
56✔
634
    return (deck);
56✔
635
}
636
//---------------------- $80 deck optimization ---------------------------------
637

638
unsigned get_required_cards_before_upgrade(std::unordered_map<unsigned, unsigned> &owned_cards,
93,277✔
639
                                           const std::vector<const Card *> &card_list, std::map<const Card *, unsigned> &num_cards)
640
{
641
    unsigned deck_cost = 0;
93,277✔
642
    std::set<const Card *> unresolved_cards;
93,277✔
643
    for (const Card *card : card_list)
417,084✔
644
    {
645
        ++num_cards[card];
323,807✔
646
        unresolved_cards.insert(card);
323,807✔
647
    }
648
    // un-upgrade according to type/category
649
    // * use fund for normal cards
650
    // * use only top-level cards for initial (basic) dominion (Alpha Dominion) and dominion material (Dominion Shard)
651
    while (!unresolved_cards.empty())
412,956✔
652
    {
653
        // pop next unresolved card
654
        auto card_it = unresolved_cards.end();
319,679✔
655
        auto card = *(--card_it);
319,679✔
656
        unresolved_cards.erase(card_it);
319,679✔
657

658
        // assume unlimited common/rare level-1 cards (standard set)
659
        if ((card->m_set == 1000) && (card->m_rarity <= 2) && (card->is_low_level_card()))
319,679✔
660
        {
661
            continue;
×
662
        }
663

664
        // keep un-defused (top-level) basic dominion & its material
665
        if ((card->m_id == 50002) || (card->m_category == CardCategory::dominion_material))
319,679✔
666
        {
667
            continue;
×
668
        }
669

670
        // defuse if inventory lacks required cards and recipe is not empty
671
        if ((fund || (card->m_category != CardCategory::normal)) && (owned_cards[card->m_id] < num_cards[card]) && !card->m_recipe_cards.empty())
319,679✔
672
        {
673
            unsigned num_under = num_cards[card] - owned_cards[card->m_id];
×
674
            num_cards[card] = owned_cards[card->m_id];
×
675

676
            // do count cost (in SP) only for normal cards
677
            if (card->m_category == CardCategory::normal)
×
678
            {
679
                deck_cost += num_under * card->m_recipe_cost;
×
680
            }
681

682
            // enqueue recipe cards as unresolved
683
            for (auto recipe_it : card->m_recipe_cards)
×
684
            {
685
                num_cards[recipe_it.first] += num_under * recipe_it.second;
×
686
                unresolved_cards.insert(recipe_it.first);
×
687
            }
688
        }
689
    }
690
    return deck_cost;
93,277✔
691
}
93,277✔
692

693
inline unsigned get_required_cards_before_upgrade(const std::vector<const Card *> &card_list, std::map<const Card *, unsigned> &num_cards)
93,277✔
694
{
695
    return get_required_cards_before_upgrade(owned_cards, card_list, num_cards);
30,503✔
696
}
697

698
unsigned get_deck_cost(const Deck *deck)
33,802✔
699
{
700
    if (!use_owned_cards)
33,802✔
701
    {
702
        return 0;
703
    }
704
    std::map<const Card *, unsigned> num_in_deck;
33,802✔
705
    unsigned deck_cost = 0;
33,802✔
706
    if (deck->commander)
33,802✔
707
    {
708
        deck_cost += get_required_cards_before_upgrade({deck->commander}, num_in_deck);
61,006✔
709
    }
710
    deck_cost += get_required_cards_before_upgrade(deck->cards, num_in_deck);
33,802✔
711
    for (auto it : num_in_deck)
230,895✔
712
    {
713
        unsigned card_id = it.first->m_id;
198,416✔
714
        if (it.second > owned_cards[card_id])
198,416✔
715
        {
716
            return UINT_MAX;
1,323✔
717
        }
718
    }
719
    return deck_cost;
720
}
33,802✔
721

722
bool is_in_recipe(const Card *card, const Card *material)
×
723
{
724
    // is it already material?
725
    if (card == material)
×
726
    {
727
        return true;
728
    }
729

730
    // no recipes
731
    if (card->m_recipe_cards.empty())
×
732
    {
733
        return false;
734
    }
735

736
    // avoid illegal
737
    if (card->m_category == CardCategory::dominion_material)
×
738
    {
739
        return false;
740
    }
741

742
    // check recursively
743
    for (auto recipe_it : card->m_recipe_cards)
×
744
    {
745
        // is material found?
746
        if (recipe_it.first == material)
×
747
        {
748
            return true;
×
749
        }
750

751
        // go deeper ...
752
        if (is_in_recipe(recipe_it.first, material))
×
753
        {
754
            return true;
755
        }
756
    }
757

758
    // found nothing
759
    return false;
760
}
761

762
bool is_owned_or_can_be_fused(const Card *card)
35,315✔
763
{
764
    if (owned_cards[card->m_id])
35,315✔
765
    {
766
        return true;
767
    }
768
    if (!fund && (card->m_category == CardCategory::normal))
35,155✔
769
    {
770
        return false;
771
    }
772
    std::map<const Card *, unsigned> num_in_deck;
×
773
    unsigned deck_cost = get_required_cards_before_upgrade({card}, num_in_deck);
×
774
    if ((card->m_category == CardCategory::normal) && (deck_cost > fund))
×
775
    {
776
        while (!card->is_low_level_card() && (deck_cost > fund))
×
777
        {
778
            card = card->downgraded();
×
779
            num_in_deck.clear();
×
780
            deck_cost = get_required_cards_before_upgrade({card}, num_in_deck);
×
781
        }
782
        if (deck_cost > fund)
×
783
        {
784
            return false;
785
        }
786
    }
787
    std::map<unsigned, unsigned> num_under;
×
788
    for (auto it : num_in_deck)
×
789
    {
790
        if (it.second > owned_cards[it.first->m_id])
×
791
        {
792
            if ((card->m_category == CardCategory::dominion_alpha) && use_maxed_dominions && !is_in_recipe(card, owned_alpha_dominion))
×
793
            {
794
                if (it.first->m_id != 50002)
×
795
                {
796
                    num_under[it.first->m_id] += it.second - owned_cards[it.first->m_id];
×
797
                }
798
                continue;
×
799
            }
800
            return false;
×
801
        }
802
    }
803
    if (!num_under.empty())
×
804
    {
805
        std::map<const Card *, unsigned> &refund = dominion_refund[owned_alpha_dominion->m_fusion_level][owned_alpha_dominion->m_level];
×
806
        for (auto &refund_it : refund)
×
807
        {
808
            unsigned refund_id = refund_it.first->m_id;
×
809
            if (!num_under.count(refund_id))
×
810
            {
811
                continue;
×
812
            }
813
            num_under[refund_id] = safe_minus(num_under[refund_id], refund_it.second);
×
814
            if (!num_under[refund_id])
×
815
            {
816
                num_under.erase(refund_id);
×
817
            }
818
        }
819
    }
820
    return num_under.empty();
×
821
}
×
822

823
std::string alpha_dominion_cost(const Card *dom_card)
×
824
{
825
    assert(dom_card->m_category == CardCategory::dominion_alpha);
×
826
    if (!owned_alpha_dominion)
×
827
    {
828
        return "(no owned alpha dominion)";
×
829
    }
830
    std::unordered_map<unsigned, unsigned> _owned_cards;
×
831
    std::unordered_map<unsigned, unsigned> refund_owned_cards;
×
832
    std::map<const Card *, unsigned> num_cards;
×
833
    std::map<const Card *, unsigned> &refund = dominion_refund[owned_alpha_dominion->m_fusion_level][owned_alpha_dominion->m_level];
×
834
    unsigned own_dom_id = 50002;
×
835
    if (is_in_recipe(dom_card, owned_alpha_dominion))
×
836
    {
837
        own_dom_id = owned_alpha_dominion->m_id;
×
838
    }
839
    else if (owned_alpha_dominion->m_id != 50002)
×
840
    {
841
        for (auto &it : refund)
×
842
        {
843
            if (it.first->m_category != CardCategory::dominion_material)
×
844
            {
845
                continue;
×
846
            }
847
            refund_owned_cards[it.first->m_id] += it.second;
×
848
        }
849
    }
850
    _owned_cards[own_dom_id] = 1;
×
851
    get_required_cards_before_upgrade(_owned_cards, {dom_card}, num_cards);
×
852
    std::string value("");
×
853
    for (auto &it : num_cards)
×
854
    {
855
        if (it.first->m_category != CardCategory::dominion_material)
×
856
        {
857
            continue;
×
858
        }
859
        value += it.first->m_name + " x " + std::to_string(it.second) + ", ";
×
860
    }
861
    if (!is_in_recipe(dom_card, owned_alpha_dominion))
×
862
    {
863
        num_cards.clear();
×
864
        get_required_cards_before_upgrade(_owned_cards, {owned_alpha_dominion}, num_cards);
×
865
        value += "using refund: ";
×
866
        for (auto &it : refund)
×
867
        {
868
            signed num_under(it.second - (signed)num_cards[it.first]);
×
869
            value += it.first->m_name + " x " + std::to_string(it.second) + "/" + std::to_string(num_under) + ", ";
×
870
        }
871
    }
872
    // remove trailing ', ' for non-empty string / replace empty by '(none)'
873
    if (!value.empty())
×
874
    {
875
        value.erase(value.end() - 2, value.end());
×
876
    }
877
    else
878
    {
879
        value += "(none)";
×
880
    }
881
    return value;
×
882
}
×
883

884
// check if claim_cards is necessary => i.e. can the deck be build from the ownedcards
885
bool claim_cards_needed(const std::vector<const Card *> &card_list)
28,962✔
886
{
887
    std::map<const Card *, unsigned> num_cards;
28,962✔
888
    get_required_cards_before_upgrade(card_list, num_cards);
28,962✔
889
    for (const auto &it : num_cards)
136,428✔
890
    {
891
        const Card *card = it.first;
109,355✔
892
        if (card->m_category == CardCategory::dominion_material && use_maxed_dominions)
109,355✔
893
            continue;
×
894
        if (card->m_category == CardCategory::dominion_alpha && use_maxed_dominions)
109,355✔
895
            continue;
×
896
        unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id]);
109,355✔
897
        if (num_to_claim > 0)
898
        {
899
            return true;
28,962✔
900
        }
901
    }
902
    return false;
903
}
28,962✔
904

905
void claim_cards(const std::vector<const Card *> &card_list)
10✔
906
{
907
    std::map<const Card *, unsigned> num_cards;
10✔
908
    get_required_cards_before_upgrade(card_list, num_cards);
10✔
909
    for (const auto &it : num_cards)
65✔
910
    {
911
        const Card *card = it.first;
55✔
912
        if (card->m_category == CardCategory::dominion_material)
55✔
913
            continue;
×
914
        if (card->m_category == CardCategory::dominion_alpha)
55✔
915
            continue;
×
916
        unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id]);
110✔
917
        if (num_to_claim > 0)
55✔
918
        {
919
            owned_cards[card->m_id] += num_to_claim;
55✔
920
            if (debug_print >= 0)
55✔
921
            {
922
                std::cerr << "WARNING: Need extra " << num_to_claim << " " << card->m_name << " to build your initial deck: adding to owned card list.\n";
55✔
923
            }
924
        }
925
    }
926
}
10✔
927

928
bool valid_deck(Deck *your_deck)
10,285✔
929
{
930
    if (claim_cards_needed({your_deck->commander}))
10,285✔
931
        return false;
932
    if (claim_cards_needed(your_deck->cards))
10,285✔
933
        return false;
934
    if (your_deck->alpha_dominion && claim_cards_needed({your_deck->alpha_dominion}))
8,396✔
935
        return false;
936
    return true; // valid
937
}
938
//------------------------------------------------------------------------------
939
FinalResults<long double> compute_score(const EvaluatedResults &results, std::vector<long double> &factors)
3,904✔
940
{
941
    FinalResults<long double> final{0, 0, 0, 0, 0, 0, results.second};
3,904✔
942
    long double max_possible = max_possible_score[(size_t)optimization_mode];
3,904✔
943
    for (unsigned index(0); index < results.first.size(); ++index)
11,670✔
944
    {
945
        // std::cout << results.second << " " << results.first[index].points << " " << results.first[index].count << std::endl;
946
        final.wins += results.first[index].wins * factors[index] * results.second / results.first[index].count;
7,766✔
947
        final.draws += results.first[index].draws * factors[index] * results.second / results.first[index].count;
7,766✔
948
        final.losses += results.first[index].losses * factors[index] * results.second / results.first[index].count;
7,766✔
949
        // APN
950
        auto trials = results.second;
7,766✔
951
        auto prob = 1 - confidence_level;
7,766✔
952
        auto successes = results.first[index].points * results.second / results.first[index].count / max_possible;
7,766✔
953
        if (successes > trials)
7,766✔
954
        {
955
            successes = trials;
×
956
            // printf("WARNING: biominal successes > trials");
957
            _DEBUG_MSG(2, "WARNING: biominal successes > trials");
×
958
        }
959

960
        auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(trials, successes, prob) * max_possible;
7,766✔
961
        auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(trials, successes, prob) * max_possible;
7,766✔
962
        if (use_harmonic_mean)
7,766✔
963
        {
964
            final.points += factors[index] / (results.first[index].points * results.second / results.first[index].count);
×
965
            final.points_lower_bound += factors[index] / lower_bound;
×
966
            final.points_upper_bound += factors[index] / upper_bound;
×
967
        }
968
        else
969
        {
970
            final.points += results.first[index].points * factors[index] * results.second / results.first[index].count;
7,766✔
971
            final.points_lower_bound += lower_bound * factors[index];
7,766✔
972
            final.points_upper_bound += upper_bound * factors[index];
7,766✔
973
        }
974
    }
975
    long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.);
3,904✔
976
    final.wins /= factor_sum * (long double)results.second;
3,904✔
977
    final.draws /= factor_sum * (long double)results.second;
3,904✔
978
    final.losses /= factor_sum * (long double)results.second;
3,904✔
979
    if (use_harmonic_mean)
3,904✔
980
    {
981
        final.points = factor_sum / ((long double)results.second * final.points);
×
982
        final.points_lower_bound = factor_sum / final.points_lower_bound;
×
983
        final.points_upper_bound = factor_sum / final.points_upper_bound;
×
984
    }
985
    else
986
    {
987
        final.points /= factor_sum * (long double)results.second;
3,904✔
988
        final.points_lower_bound /= factor_sum;
3,904✔
989
        final.points_upper_bound /= factor_sum;
3,904✔
990
    }
991
    return final;
3,904✔
992
}
993
//------------------------------------------------------------------------------
994
//------------------------------------------------------------------------------
995
// Per thread data.
996
// seed should be unique for each thread.
997
// d1 and d2 are intended to point to read-only process-wide data.
998

999
void SimulationData::set_decks(std::vector<Deck *> const your_decks_, std::vector<Deck *> const &enemy_decks_)
3,513✔
1000
{
1001
    for (unsigned i(0); i < your_decks_.size(); ++i)
7,452✔
1002
    {
1003
        your_decks[i].reset(your_decks_[i]->clone());
3,939✔
1004
        your_hands[i]->deck = your_decks[i].get();
3,939✔
1005
    }
1006
    for (unsigned i(0); i < enemy_decks_.size(); ++i)
7,452✔
1007
    {
1008
        enemy_decks[i].reset(enemy_decks_[i]->clone());
3,939✔
1009
        enemy_hands[i]->deck = enemy_decks[i].get();
3,939✔
1010
    }
1011
}
3,513✔
1012

1013
std::vector<std::unordered_map<std::string, std::string>> Process::samples()
1✔
1014
{
1015
    std::vector<std::unordered_map<std::string, std::string>> samples;
1✔
1016

1017
    int i = 0;
1✔
1018
    // loop over partial ids and prefix with f add to fmap
1019
    auto ids = partial_ids();
1✔
1020
    for (auto const &ydeck : your_decks)
2✔
1021
    {
1022
        auto yids = ydeck->sorted_ids();
1✔
1023
        for (auto const &edeck : enemy_decks)
2✔
1024
        {
1025
            auto eids = edeck->sorted_ids();
1✔
1026
            std::unordered_map<std::string, std::string> fmap;
1✔
1027
            i = 0;
1✔
1028
            for (auto const &id : ids)
91✔
1029
            {
1030
                fmap.emplace("f" + std::to_string(i), std::to_string(id));
180✔
1031
                i++;
90✔
1032
            }
1033
            i = 0;
1✔
1034
            for (auto const &id : yids)
13✔
1035
            {
1036
                fmap.emplace("y" + std::to_string(i), std::to_string(id->m_id));
24✔
1037
                i++;
12✔
1038
            }
1039
            // fill up with zeros up to id 12
1040
            while (i < 12)
1✔
1041
            {
1042
                fmap.emplace("y" + std::to_string(i), std::to_string(0));
×
1043
                i++;
×
1044
            }
1045
            i = 0;
1✔
1046
            for (auto const &id : eids)
13✔
1047
            {
1048
                fmap.emplace("e" + std::to_string(i), std::to_string(id->m_id));
24✔
1049
                i++;
12✔
1050
            }
1051
            // fill up with zeros up to id 12
1052
            while (i < 12)
1✔
1053
            {
1054
                fmap.emplace("e" + std::to_string(i), std::to_string(0));
×
1055
                i++;
×
1056
            }
1057
            samples.emplace_back(fmap);
1✔
1058
        }
1✔
1059
    }
1✔
1060
    /*
1061
    auto myMap = samples.at(0);
1062
    for(auto it = myMap.begin(); it != myMap.end(); ++it)
1063
    {
1064
        std::cout << it->first << " " << it->second  <<"\n";
1065
    }
1066
    */
1067

1068
    return samples;
1✔
1069
}
1✔
1070

1071
std::vector<std::array<std::string, 3>> Process::hashes()
3,439✔
1072
{
1073
    std::vector<std::array<std::string, 3>> hashes;
3,439✔
1074
    hashes.reserve(your_decks.size() * enemy_decks.size());
3,439✔
1075
    std::string hash = partial_hash();
3,439✔
1076
    for (auto const &ydeck : your_decks)
7,097✔
1077
    {
1078
        for (auto const &edeck : enemy_decks)
9,028✔
1079
        {
1080
            hashes.emplace_back(std::array<std::string, 3>{hash, ydeck->hash(), edeck->hash()});
16,110✔
1081
        }
1082
    }
1083
    return hashes;
3,439✔
1084
}
3,439✔
1085

1086
std::vector<unsigned int> Process::partial_ids()
3,440✔
1087
{
1088
    std::vector<unsigned int> ids;
3,440✔
1089
    ids.emplace_back(gamemode);
3,440✔
1090
    ids.emplace_back(optimization_mode);
3,440✔
1091
    for (int i = 0; i < Fix::num_fixes; ++i)
44,720✔
1092
    {
1093
        ids.emplace_back(fixes[i]);
41,280✔
1094
    }
1095
    ids.emplace_back(flexible_iter);
3,440✔
1096
    ids.emplace_back(flexible_turn);
3,440✔
1097
    ids.emplace_back(eval_iter);
3,440✔
1098
    ids.emplace_back(eval_turn);
3,440✔
1099
    for (int i = 0; i < PassiveBGE::num_passive_bges; ++i)
96,320✔
1100
        ids.emplace_back(your_bg_effects[i]);
92,880✔
1101
    for (int i = 0; i < PassiveBGE::num_passive_bges; ++i)
96,320✔
1102
        ids.emplace_back(enemy_bg_effects[i]);
92,880✔
1103
    if (your_bg_skills.size() > 0)
3,440✔
1104
    {
1105
        auto bge = your_bg_skills[0];
1✔
1106
        ids.emplace_back(bge.id);
1✔
1107
        ids.emplace_back(bge.x);
1✔
1108
        ids.emplace_back(bge.y);
1✔
1109
        ids.emplace_back(bge.n);
1✔
1110
        ids.emplace_back(bge.c);
1✔
1111
        ids.emplace_back(bge.s);
1✔
1112
        ids.emplace_back(bge.s2);
1✔
1113
        ids.emplace_back(bge.all);
1✔
1114
        ids.emplace_back(bge.card_id);
1✔
1115
    }
1116
    else
1117
    {
1118
        // emplace back 9 zeros
1119
        for (int i = 0; i < 9; ++i)
34,390✔
1120
            ids.emplace_back(0);
30,951✔
1121
    }
1122
    if (enemy_bg_skills.size() > 0)
3,440✔
1123
    {
1124
        auto bge = enemy_bg_skills[0];
1✔
1125
        ids.emplace_back(bge.id);
1✔
1126
        ids.emplace_back(bge.x);
1✔
1127
        ids.emplace_back(bge.y);
1✔
1128
        ids.emplace_back(bge.n);
1✔
1129
        ids.emplace_back(bge.c);
1✔
1130
        ids.emplace_back(bge.s);
1✔
1131
        ids.emplace_back(bge.s2);
1✔
1132
        ids.emplace_back(bge.all);
1✔
1133
        ids.emplace_back(bge.card_id);
1✔
1134
    }
1135
    else
1136
    {
1137
        // emplace back 9 zeros
1138
        for (int i = 0; i < 9; ++i)
34,390✔
1139
            ids.emplace_back(0);
30,951✔
1140
    }
1141

1142
    return ids;
3,440✔
1143
}
×
1144

1145
// Compute field hash from two decks
1146
std::string Process::partial_hash()
3,439✔
1147
{
1148
    // TODO fail if quest enabled since, db caching is incompatible with quest mode
1149
    std::stringstream ios;
3,439✔
1150
    for (auto id : partial_ids())
312,949✔
1151
        encode_id_ext_b64(ios, id);
312,949✔
1152

1153
    return ios.str();
6,878✔
1154
}
3,439✔
1155

1156
inline std::vector<Results<uint64_t>> SimulationData::evaluate(const std::vector<bool> &deck_mask)
1,212,873✔
1157
{
1158
    std::vector<Results<uint64_t>> res;
1,212,873✔
1159
    res.reserve(enemy_hands.size() * your_hands.size());
1,212,873✔
1160
    assert(deck_mask.size() == your_hands.size() * enemy_hands.size());
1,212,873✔
1161
    int i = 0;
1162
    for (Hand *your_hand : your_hands)
2,446,446✔
1163
    {
1164
        for (Hand *enemy_hand : enemy_hands)
2,629,946✔
1165
        {
1166
            // TUO5 added mask to already computed decks from db
1167
            if (deck_mask[i] != 0)
1,396,373✔
1168
            {
1169
                your_hand->reset(re);
1,396,373✔
1170
                enemy_hand->reset(re);
1,396,373✔
1171
                Field fd(re, cards, *your_hand, *enemy_hand, gamemode, optimization_mode,
1,396,373✔
1172
#ifndef NQUEST
1173
                         quest,
1174
#endif
1175
                         your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills, fixes, flexible_iter, flexible_turn, eval_iter, eval_turn);
1,396,373✔
1176
                Results<uint64_t> result(play(&fd));
1,396,373✔
1177
                if (__builtin_expect(mode_open_the_deck, false))
1,396,373✔
1178
                {
1179
                    // are there remaining (unopened) cards?
1180
                    if (fd.players[1]->deck->shuffled_cards.size())
×
1181
                    {
1182
                        // apply min score (there are unopened cards, so mission failed)
1183
                        result.points = min_possible_score[(size_t)optimization_mode];
×
1184
                    }
1185
                }
1186
                res.emplace_back(result);
1,396,373✔
1187
            }
1,396,373✔
1188
            else
1189
            {
1190
                res.emplace_back(Results<uint64_t>()); // no change since masked
×
1191
            }
1192
            i++;
1,396,373✔
1193
        }
1194
    }
1195
    // std::cout << std::endl<<  "Deck hash: " << your_hand.deck->hash() << "#"<< std::endl;
1196
    return (res);
1,212,873✔
1197
}
×
1198

1199
inline std::vector<Results<uint64_t>> SimulationData::evaluate()
1200
{
1201
    return evaluate(std::vector<bool>(your_hands.size() * enemy_hands.size(), true));
1202
}
1203
//------------------------------------------------------------------------------
1204

1205
//------------------------------------------------------------------------------
1206
// Process
1207
#ifdef _OPENMP
1208
void Process::openmp_evaluate_reduction(EvaluatedResults &evaluated_results)
1209
{
1210

1211
    std::vector<Results<uint64_t>> save_results(evaluated_results.first);
1212
    std::vector<Results<uint64_t>> results(evaluated_results.first);
1213
#pragma omp declare reduction(VecPlus                            \
1214
                              : std::vector <Results <uint64_t>> \
1215
                              : omp_out = merge(omp_out, omp_in))
1216
#pragma omp parallel default(none) shared(evaluated_results, thread_num_iterations, results)
1217
    {
1218
        SimulationData *sim = threads_data.at(omp_get_thread_num());
1219
        sim->set_decks(this->your_decks, this->enemy_decks);
1220
#pragma omp for reduction(VecPlus \
1221
                          : results) schedule(runtime)
1222
        for (unsigned i = 0; i < thread_num_iterations; ++i)
1223
        {
1224
            if (results.size() == 0)
1225
                results = sim->evaluate(mask); // calculate single sim
1226
            else
1227
            {
1228
                results = merge(results, sim->evaluate(mask)); // calculate single sim
1229
            }
1230
        }
1231
#pragma omp for schedule(runtime)
1232
        for (unsigned i = 0; i < results.size(); ++i)
1233
            evaluated_results.first[i] = results[i]; //+?
1234
    }
1235
    evaluated_results.second += thread_num_iterations;
1236
}
1237

1238
void Process::openmp_compare_reduction(EvaluatedResults &evaluated_results)
1239
{
1240

1241
    const unsigned c_num_threads = opt_num_threads;
1242
    std::vector<Results<uint64_t>> save_results(evaluated_results.first);
1243
    std::vector<Results<uint64_t>> results(evaluated_results.first);
1244
    bool compare_stop{false};
1245
    unsigned trials[c_num_threads] = {0};
1246
    long double successes[c_num_threads] = {.0};
1247
    const long double max_possible = max_possible_score[(size_t)optimization_mode];
1248
    const long double prob = 1 - confidence_level;
1249
    omp_lock_t locks[c_num_threads];
1250
    for (unsigned i = 0; i < c_num_threads; ++i)
1251
        omp_init_lock(&locks[i]);
1252
#pragma omp declare reduction(VecPlus                            \
1253
                              : std::vector <Results <uint64_t>> \
1254
                              : omp_out = merge(omp_out, omp_in))
1255
#pragma omp parallel default(none) shared(evaluated_results, trials, successes, locks, compare_stop, thread_num_iterations, results, \
1256
                                          thread_best_results, min_increment_of_score)
1257
    {
1258
        SimulationData *sim = threads_data.at(omp_get_thread_num());
1259
        sim->set_decks(this->your_decks, this->enemy_decks);
1260
        const long double sim_accum = std::accumulate(sim->factors.begin(), sim->factors.end(), .0);
1261
#pragma omp for reduction(VecPlus \
1262
                          : results) schedule(runtime)
1263
        for (unsigned i = 0; i < thread_num_iterations; ++i)
1264
        {
1265
#pragma omp cancellation point for
1266
            if (!compare_stop)
1267
            {
1268
                if (results.size() == 0)
1269
                    results = sim->evaluate(mask); // calculate single sim
1270
                else
1271
                    results = merge(results, sim->evaluate(mask)); // calculate single sim
1272
                long double score_accum_d = 0.0;
1273
                for (unsigned j = 0; j < results.size(); ++j)
1274
                    score_accum_d += results[j].points * sim->factors[j];
1275
                score_accum_d /= sim_accum * max_possible;
1276
                omp_set_lock(&locks[omp_get_thread_num()]);
1277
                trials[omp_get_thread_num()]++;
1278
                successes[omp_get_thread_num()] = score_accum_d;
1279
                omp_unset_lock(&locks[omp_get_thread_num()]);
1280
                // #pragma omp master
1281
                if (omp_get_thread_num() == 0)
1282
                {
1283

1284
                    unsigned ttrials = 0;
1285
                    long double ssuccesses = 0.0;
1286
                    for (unsigned l = 0; l < c_num_threads; ++l)
1287
                    {
1288
                        omp_set_lock(&locks[l]);
1289
                        ttrials += trials[l];
1290
                        ssuccesses += successes[l];
1291
                        omp_unset_lock(&locks[l]);
1292
                    }
1293
                    if (ssuccesses > ttrials)
1294
                    {
1295
                        ssuccesses = ttrials;
1296
                        printf("WARNING: biominal successes {%Le} > trials {%d} in Threads\n", ssuccesses, ttrials);
1297
                        //_DEBUG_MSG(2,"WARNING: biominal successes > trials in Threads");
1298
                    }
1299
                    compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(ttrials, ssuccesses, prob) * max_possible <
1300
                                    thread_best_results->points + min_increment_of_score);
1301
                    if (compare_stop)
1302
                    {
1303
#pragma omp cancel for
1304
                    }
1305
                }
1306
            }
1307
        }
1308
#pragma omp for schedule(runtime)
1309
        for (unsigned i = 0; i < results.size(); ++i)
1310
            evaluated_results.first[i] = results[i]; //+?
1311
    }
1312
    evaluated_results.second += thread_num_iterations;
1313
    for (unsigned i = 0; i < c_num_threads; ++i)
1314
        omp_destroy_lock(&locks[i]);
1315
}
1316

1317
std::vector<Results<uint64_t>> merge(std::vector<Results<uint64_t>> out, std::vector<Results<uint64_t>> in)
1318
{
1319
    // printf("merging out: %d in: %d \n", (int)out[0].wins, (int)in[0].wins);
1320
    // printf("out%p ",(void *)&out );
1321
    for (unsigned i = 0; i < out.size(); ++i)
1322
        out[i] += in[i];
1323
    // printf("merged out: %d \n", (int)out[0].wins);
1324
    return out;
1325
}
1326
#endif
1327

1328
inline bool Process::eval_ml(unsigned num_iterations, EvaluatedResults &evaluated_results)
3,436✔
1329
{
1330
    if (!use_ml)
3,436✔
1331
        return true;
1332
    bool run = false;
1✔
1333
    auto sampls = samples();
1✔
1334
    // iterate results from samples()
1335
    for (unsigned i = 0; i < sampls.size(); i++)
2✔
1336
    {
1337
        // already found in db?
1338
        if (mask[i])
1✔
1339
        {
1340
            auto s = sampls[i];
1✔
1341
            auto wins = atof(win_model.predict(s).c_str());
1✔
1342
            auto losses = atof(loss_model.predict(s).c_str());
1✔
1343
            auto draws = atof(stall_model.predict(s).c_str());
1✔
1344
            auto points = atof(points_model.predict(s).c_str());
1✔
1345
            if (wins < 0)
1✔
1346
                wins = 0;
1347
            if (losses < 0)
1✔
1348
                losses = 0;
1349
            if (draws < 0)
1✔
1350
                draws = 0;
1351
            if (points < 0)
1✔
1352
                points = 0;
1353
            // if((wins + losses + draws) > 1.05 || (wins + losses + draws) < 0.95) {
1354
            //     std::cout << "WARNING: ML model returned invalid sample: " << wins << " "  << draws << " " <<losses << " points " << points << std::endl;
1355
            // }
1356
            if (use_only_ml || ((wins + losses + draws) < 1.00 + ml_precision && (wins + losses + draws) > 1.00 - ml_precision)) // we use the sum of wins, losses and draws to determine if the sample is valid
1✔
1357
            {
1358
                // std::cout << "ML model returned: " << wins << " "  << draws << " " <<losses << " points " << points << " " << num_iterations << std::endl;
1359
                mask[i] = false;
1✔
1360
                Results<uint64_t> r = {
1✔
1361
                    static_cast<uint64_t>(std::round((1.0 * wins * num_iterations / (wins + losses + draws)))),
1✔
1362
                    static_cast<uint64_t>(std::round((1.0 * draws * num_iterations / (wins + losses + draws)))),
1✔
1363
                    static_cast<uint64_t>(std::round((1.0 * losses * num_iterations / (wins + losses + draws)))),
1✔
1364
                    static_cast<uint64_t>(std::round((1.0 * points * num_iterations))),
1✔
1365
                    static_cast<uint64_t>(num_iterations)};
1✔
1366
                evaluated_results.first[i] = r;
1✔
1367
            }
1✔
1368
            else
1369
            {
1370
                mask[i] = true;
×
1371
                run = true;
×
1372
            }
1373
        }
1✔
1374
    }
1375

1376
    return run;
1✔
1377
}
1✔
1378

1379
inline bool Process::check_db(std::vector<std::array<std::string, 3>> const &vhashes, unsigned num_iterations, EvaluatedResults &evaluated_results)
3,439✔
1380
{
1381
    if (!use_db_load)
3,439✔
1382
        return true;
1383
    bool run = false;
1384
    // TODO TUO5 check db for results
1385
    //  TODO TUO5 calculate mask for sim evaluate based on missing results in db
1386
    for (unsigned i = 0; i < vhashes.size(); i++)
2,049✔
1387
    {
1388
        bool found = false;
1,990✔
1389
        std::string hash = vhashes[i][0];
1,990✔
1390
        // TODO TUO5 check db for hash
1391
        auto it1 = database.find(hash);
1,990✔
1392
        std::map<std::string, Results<uint64_t>>::iterator it;
1,990✔
1393
        if (it1 != database.end())
1,990✔
1394
        {
1395
            auto it2 = it1->second.find(vhashes[i][1]);
568✔
1396
            if (it2 != it1->second.end())
568✔
1397
            {
1398
                auto it3 = it2->second.find(vhashes[i][2]);
100✔
1399
                if (it3 != it2->second.end())
100✔
1400
                {
1401
                    it = it3;
99✔
1402
                    found = true;
99✔
1403
                    // std::cout << "hit databse" << std::endl;
1404
                }
1405
            }
1406
        }
1407
        // TODO TUO5 if hash not in db, add to mask
1408
        if (!found)
99✔
1409
        {
1410
            mask[i] = true;
1,891✔
1411
            run = true;
1,891✔
1412
        }
1413
        else
1414
        {
1415
            if (it->second.count >= num_iterations)
99✔
1416
            {
1417
                mask[i] = false;
99✔
1418
                // TUO5 load results from db
1419
                // renormalized to asked num_iterations
1420
                Results<uint64_t> r = {
99✔
1421
                    static_cast<uint64_t>(std::round((1.0 * it->second.wins * num_iterations) / it->second.count)),
99✔
1422
                    static_cast<uint64_t>(std::round((1.0 * it->second.draws * num_iterations) / it->second.count)),
99✔
1423
                    static_cast<uint64_t>(std::round((1.0 * it->second.losses * num_iterations) / it->second.count)),
99✔
1424
                    static_cast<uint64_t>(std::round((1.0 * it->second.points * num_iterations) / it->second.count)),
99✔
1425
                    static_cast<uint64_t>(num_iterations)};
99✔
1426
                evaluated_results.first[i] = r;
99✔
1427
            }
1428
            else
1429
            {
1430
                mask[i] = true;
×
1431
                run = true;
×
1432
            }
1433
        }
1434
    }
1,990✔
1435
    return run;
1436
}
1437

1438
inline void Process::save_db(std::vector<std::array<std::string, 3>> const &vhashes, EvaluatedResults &evaluated_results)
3,435✔
1439
{
1440
    // TODO TUO5 save results to db
1441
    for (unsigned i = 0; i < vhashes.size(); i++)
8,705✔
1442
    {
1443
        if (mask[i])
5,270✔
1444
        {
1445
            std::array<std::string, 3> hash = vhashes[i];
5,270✔
1446
            // check if map is initialized otherwise initialize
1447
            if (database.find(hash[0]) == database.end())
5,270✔
1448
            {
1449
                database[hash[0]] = std::map<std::string, std::map<std::string, Results<uint64_t>>>();
48✔
1450
            }
1451
            if (database[hash[0]].find(hash[1]) == database[hash[0]].end())
5,270✔
1452
            {
1453
                database[hash[0]][hash[1]] = std::map<std::string, Results<uint64_t>>();
2,631✔
1454
            }
1455
            database[hash[0]][hash[1]][hash[2]] = evaluated_results.first[i];
5,270✔
1456
        }
5,270✔
1457
    }
1458
}
3,435✔
1459

1460
EvaluatedResults &Process::evaluate(unsigned num_iterations, EvaluatedResults &evaluated_results)
412✔
1461
{
1462
    if (num_iterations <= evaluated_results.second)
412✔
1463
    {
1464
        return evaluated_results;
1465
    }
1466
    std::vector<std::array<std::string, 3>> vhashes = hashes();
250✔
1467
    // fill mask with true
1468
    mask = std::vector<bool>(vhashes.size(), true);
250✔
1469
    if (!check_db(vhashes, num_iterations, evaluated_results))
250✔
1470
    {
1471
        // 100% covered by db
1472
        evaluated_results.second = num_iterations;
3✔
1473
        return evaluated_results;
3✔
1474
    }
1475
    if (!eval_ml(num_iterations, evaluated_results))
247✔
1476
    {
1477
        // ml is good
1478
        evaluated_results.second = num_iterations;
1✔
1479
        return evaluated_results;
1✔
1480
    }
1481

1482
    thread_num_iterations = num_iterations - evaluated_results.second;
246✔
1483
    thread_results = &evaluated_results;
246✔
1484
    thread_compare = false;
246✔
1485
#ifndef _OPENMP
1486
    // unlock all the threads
1487
    main_barrier.wait();
246✔
1488
    // wait for the threads
1489
    main_barrier.wait();
246✔
1490
#else
1491
    openmp_evaluate_reduction(evaluated_results);
1492
#endif
1493
    save_db(vhashes, evaluated_results);
246✔
1494

1495
    return evaluated_results;
1496
}
250✔
1497

1498
EvaluatedResults &Process::compare(unsigned num_iterations, EvaluatedResults &evaluated_results, const FinalResults<long double> &best_results)
3,383✔
1499
{
1500
    if (num_iterations <= evaluated_results.second)
3,383✔
1501
    {
1502
        return evaluated_results;
1503
    }
1504
    std::vector<std::array<std::string, 3>> vhashes = hashes();
3,189✔
1505
    // fill mask with true
1506
    mask = std::vector<bool>(vhashes.size(), true);
3,189✔
1507
    if (!check_db(vhashes, num_iterations, evaluated_results))
3,189✔
1508
    {
1509
        // 100% covered by db
1510
        evaluated_results.second = num_iterations;
×
1511
        return evaluated_results;
×
1512
    }
1513
    if (!eval_ml(num_iterations, evaluated_results))
3,189✔
1514
    {
1515
        // ml is good
1516
        evaluated_results.second = num_iterations;
×
1517
        return evaluated_results;
×
1518
    }
1519
    thread_num_iterations = num_iterations - evaluated_results.second;
3,189✔
1520
    thread_results = &evaluated_results;
3,189✔
1521
    thread_best_results = &best_results;
3,189✔
1522
    thread_compare = true;
3,189✔
1523
    thread_compare_stop = false;
3,189✔
1524
#ifndef _OPENMP
1525
    // unlock all the threads
1526
    main_barrier.wait();
3,189✔
1527
    // wait for the threads
1528
    main_barrier.wait();
3,189✔
1529
#else
1530
    openmp_compare_reduction(evaluated_results);
1531
#endif
1532
    save_db(vhashes, evaluated_results);
3,189✔
1533
    return evaluated_results;
1534
}
3,189✔
1535
//------------------------------------------------------------------------------
1536
void thread_evaluate(boost::barrier &main_barrier,
78✔
1537
                     boost::mutex &shared_mutex,
1538
                     SimulationData &sim,
1539
                     const Process &p,
1540
                     unsigned thread_id)
1541
{
1542
#ifndef _OPENMP
1543
    while (true)
6,948✔
1544
    {
1545
        main_barrier.wait();
3,513✔
1546
        sim.set_decks(p.your_decks, p.enemy_decks);
3,513✔
1547
        if (destroy_threads)
3,513✔
1548
        {
1549
            return;
78✔
1550
        }
1551
        while (true)
2,429,181✔
1552
        {
1553
            shared_mutex.lock();                                                       //<<<<
1,216,308✔
1554
            if (thread_num_iterations == 0 || (thread_compare && thread_compare_stop)) //!
1,216,308✔
1555
            {
1556
                shared_mutex.unlock(); //>>>>
3,435✔
1557
                main_barrier.wait();
3,435✔
1558
                break;
3,435✔
1559
            }
1560
            else
1561
            {
1562
                --thread_num_iterations; //!
1,212,873✔
1563
                shared_mutex.unlock();   //>>>>
1,212,873✔
1564
                std::vector<Results<uint64_t>> result{sim.evaluate(p.mask)};
1,212,873✔
1565
                shared_mutex.lock();                                                        //<<<<
1,212,873✔
1566
                std::vector<uint64_t> thread_score_local(thread_results->first.size(), 0u); //!
1,212,873✔
1567
                unsigned counter = 0;
1,212,873✔
1568
                for (unsigned index(0); index < result.size(); ++index)
2,609,246✔
1569
                {
1570
                    thread_results->first[index] += result[index]; //!
1,396,373✔
1571
                    if (counter == 0 || thread_results->first[index].count < counter)
1,396,373✔
1572
                    {
1573
                        counter = thread_results->first[index].count;
1,212,873✔
1574
                    }
1575
                }
1576
                for (unsigned index(0); index < result.size(); ++index)
2,609,246✔
1577
                {
1578
                    // TUO5 we weight scores by the number of times a deck is used, since the results are no langer same sized in partially cached case
1579
                    thread_score_local[index] = (thread_results->first[index].points * (counter)) / thread_results->first[index].count; //!
1,396,373✔
1580
                                                                                                                                        // thread_score_local[index] = static_cast<uint64_t>(std::round((thread_results->first[index].points * (counter))/thread_results->first[index].count)); //!
1581
                }
1582
                ++thread_results->second;                            //!
1,212,873✔
1583
                unsigned thread_total_local{thread_results->second}; //!
1,212,873✔
1584
                shared_mutex.unlock();                               //>>>>
1,212,873✔
1585
                if (thread_compare && thread_id == 0 && thread_total_local > 1)
1,212,873✔
1586
                {
1587
                    unsigned score_accum = 0;
819,992✔
1588
                    // Multiple defense decks case: scaling by factors and approximation of a "discrete" number of events.
1589
                    if (result.size() > 1)
819,992✔
1590
                    {
1591
                        long double score_accum_d = 0.0;
1592
                        for (unsigned i = 0; i < thread_score_local.size(); ++i)
×
1593
                        {
1594
                            score_accum_d += thread_score_local[i] * sim.factors[i];
×
1595
                        }
1596
                        score_accum_d /= std::accumulate(sim.factors.begin(), sim.factors.end(), .0);
×
1597
                        score_accum = score_accum_d;
×
1598
                    }
1599
                    else
1600
                    {
1601
                        score_accum = thread_score_local[0];
819,992✔
1602
                    }
1603
                    bool compare_stop(false);
819,992✔
1604
                    long double max_possible = max_possible_score[(size_t)optimization_mode];
819,992✔
1605

1606
                    // APN
1607
                    auto trials = thread_total_local;
819,992✔
1608
                    auto prob = 1 - confidence_level;
819,992✔
1609
                    auto successes = score_accum / max_possible;
819,992✔
1610
                    if (successes > trials)
819,992✔
1611
                    {
1612
                        std::cout << "WARNING: biominal successes > trials in Threads" << successes << " " << trials << " " << counter << std::endl;
×
1613
                        _DEBUG_MSG(2, "WARNING: biominal successes > trials in Threads");
×
1614
                        successes = trials;
1,212,873✔
1615
                    }
1616
                    else
1617
                    {
1618

1619
                        // Get a loose (better than no) upper bound. TODO: Improve it.
1620
                        compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(trials, successes, prob) * max_possible <
819,992✔
1621
                                        thread_best_results->points + min_increment_of_score);
819,992✔
1622
                        if (compare_stop)
819,992✔
1623
                        {
1624
                            shared_mutex.lock(); //<<<<
2,714✔
1625
                            // std::cout << thread_total_local << "\n";
1626
                            thread_compare_stop = true; //!
2,714✔
1627
                            shared_mutex.unlock();      //>>>>
2,714✔
1628
                        }
1629
                    }
1630
                }
1631
            }
1,212,873✔
1632
        }
1,212,873✔
1633
    }
3,435✔
1634
#endif
1635
}
1636
//------------------------------------------------------------------------------
1637
void print_score_info(const EvaluatedResults &results, std::vector<long double> &factors)
36✔
1638
{
1639
    auto final = compute_score(results, factors);
36✔
1640
    std::cout << final.points << " (";
36✔
1641
    if (!simplify_output)
36✔
1642
    {
1643
        if (show_ci)
36✔
1644
        {
1645
            std::cout << final.points_lower_bound << "-" << final.points_upper_bound << ", ";
×
1646
        }
1647
        for (const auto &val : results.first)
72✔
1648
        {
1649
            switch (optimization_mode)
36✔
1650
            {
1651
            case OptimizationMode::raid:
×
1652
            case OptimizationMode::campaign:
×
1653
            case OptimizationMode::brawl:
×
1654
            case OptimizationMode::brawl_defense:
×
1655
            case OptimizationMode::war:
×
1656
            case OptimizationMode::war_defense:
×
1657
#ifndef NQUEST
1658
            case OptimizationMode::quest:
1659
#endif
1660
                std::cout << val.points << " ";
×
1661
                break;
1662
            default:
36✔
1663
                std::cout << val.points / 100 << " ";
36✔
1664
                break;
1665
            }
1666
        }
1667
    }
1668
    else
1669
    {
1670
        std::cout << "...";
×
1671
    }
1672
    std::cout << "/ " << results.second << ")" << std::endl;
36✔
1673
}
36✔
1674
//------------------------------------------------------------------------------
1675
void print_results(const EvaluatedResults &results, std::vector<long double> &factors)
73✔
1676
{
1677
    auto final = compute_score(results, factors);
73✔
1678
    std::cout << "win%: " << final.wins * 100.0 << " (";
73✔
1679
    if (!simplify_output)
73✔
1680
    {
1681
        for (const auto &val : results.first)
2,077✔
1682
        {
1683
            std::cout << val.wins << " ";
2,004✔
1684
        }
1685
    }
1686
    else
1687
    {
1688
        std::cout << "...";
×
1689
    }
1690
    std::cout << "/ " << results.second << ")" << std::endl;
73✔
1691

1692
    std::cout << "stall%: " << final.draws * 100.0 << " (";
73✔
1693
    if (!simplify_output)
73✔
1694
    {
1695
        for (const auto &val : results.first)
2,077✔
1696
        {
1697
            std::cout << val.draws << " ";
2,004✔
1698
        }
1699
    }
1700
    else
1701
    {
1702
        std::cout << "...";
×
1703
    }
1704
    std::cout << "/ " << results.second << ")" << std::endl;
73✔
1705

1706
    std::cout << "loss%: " << final.losses * 100.0 << " (";
73✔
1707
    if (!simplify_output)
73✔
1708
    {
1709
        for (const auto &val : results.first)
2,077✔
1710
        {
1711
            std::cout << val.losses << " ";
2,004✔
1712
        }
1713
    }
1714
    else
1715
    {
1716
        std::cout << "...";
×
1717
    }
1718
    std::cout << "/ " << results.second << ")" << std::endl;
73✔
1719

1720
#ifndef NQUEST
1721
    if (optimization_mode == OptimizationMode::quest)
1722
    {
1723
        // points = win% * win_score + (must_win ? win% : 100%) * quest% * quest_score
1724
        // quest% = (points - win% * win_score) / (must_win ? win% : 100%) / quest_score
1725
        std::cout << "quest%: " << (final.points - final.wins * tuo::quest.win_score) / (tuo::quest.must_win ? final.wins : 1) / tuo::quest.quest_score * 100 << std::endl;
1726
    }
1727
#endif
1728

1729
    unsigned min_score = min_possible_score[(size_t)optimization_mode];
73✔
1730
    unsigned max_score = max_possible_score[(size_t)optimization_mode];
73✔
1731
    switch (optimization_mode)
73✔
1732
    {
1733
    case OptimizationMode::raid:
×
1734
    case OptimizationMode::campaign:
×
1735
    case OptimizationMode::brawl:
×
1736
    case OptimizationMode::brawl_defense:
×
1737
    case OptimizationMode::war:
×
1738
    case OptimizationMode::war_defense:
×
1739
#ifndef NQUEST
1740
    case OptimizationMode::quest:
1741
#endif
1742
        std::cout << "score: " << final.points;
×
1743
        if (optimization_mode == OptimizationMode::brawl)
×
1744
        {
1745
            auto win_points = final.wins ? ((final.points - min_score * (1.0 - final.wins)) / final.wins) : final.points;
×
1746
            std::cout << " [" << win_points << " per win]";
×
1747
        }
1748
        else if (optimization_mode == OptimizationMode::brawl_defense)
×
1749
        {
1750
            auto opp_win_points = final.losses ? max_score - ((final.points - (max_score - min_score) * (1.0 - final.losses)) / final.losses) : final.points;
×
1751
            std::cout << " [" << opp_win_points << " per opp win]";
×
1752
        }
1753
        else if (optimization_mode == OptimizationMode::war)
×
1754
        {
1755
            auto win_points = final.wins ? ((final.points - min_score * (1.0 - final.wins)) / final.wins) : final.points;
×
1756
            std::cout << " [" << win_points << " per win]";
×
1757
        }
1758
        else if (optimization_mode == OptimizationMode::war_defense)
×
1759
        {
1760
            auto opp_win_points = final.losses ? max_score - ((final.points - (max_score - min_score) * (1.0 - final.losses)) / final.losses) : final.points;
×
1761
            std::cout << " [" << opp_win_points << " per opp win]";
×
1762
        }
1763
        std::cout << " (";
×
1764
        if (!simplify_output)
×
1765
        {
1766
            for (const auto &val : results.first)
×
1767
            {
1768
                std::cout << val.points << " ";
×
1769
            }
1770
        }
1771
        else
1772
        {
1773
            std::cout << "...";
×
1774
        }
1775
        std::cout << "/ " << results.second << ")" << std::endl;
×
1776
        if (show_ci)
×
1777
        {
1778
            std::cout << "ci: " << final.points_lower_bound << " - " << final.points_upper_bound << std::endl;
×
1779
        }
1780
        break;
1781
    default:
1782
        break;
1783
    }
1784
}
73✔
1785

1786
//------------------------------------------------------------------------------
1787
// Prints which cards have been upgraded
1788
void print_upgraded_cards(Deck *deck)
5✔
1789
{
1790
    if (!print_upgraded)
5✔
1791
        return;
5✔
1792
    auto owned_cards_c = owned_cards;
×
1793
    std::cout << "Upgraded Cards: ";
×
1794
    auto cards = deck->cards;
×
1795
    cards.insert(cards.begin(), deck->commander);
×
1796
    for (const Card *card : cards)
×
1797
    {
1798

1799
        if ((card->m_set == 1000) && (card->m_rarity <= 2) && (card->is_low_level_card()))
×
1800
        {
1801
            continue;
×
1802
        }
1803

1804
        if ((card->m_id == 50002) || (card->m_category == CardCategory::dominion_material))
×
1805
        {
1806
            continue;
×
1807
        }
1808

1809
        if ((fund || (card->m_category != CardCategory::normal)) && (owned_cards_c[card->m_id] == 0))
×
1810
        {
1811
            // print card
1812
            std::cout << card->m_name << ", ";
×
1813
        }
1814
        if (owned_cards_c[card->m_id] > 0)
×
1815
            owned_cards_c[card->m_id]--;
×
1816
    }
1817
    std::cout << std::endl;
×
1818
}
×
1819
//------------------------------------------------------------------------------
1820
void print_cards_inline(std::vector<const Card *> cards, std::ostream &os, Deck *deck)
1,269✔
1821
{
1822
    std::string last_name = "";
1,269✔
1823
    unsigned num_repeat(0);
1,269✔
1824
    bool first = true;
1,269✔
1825

1826
    // for (const Card* card: cards)
1827
    //{
1828
    for (unsigned i = 0; i < cards.size(); ++i)
13,604✔
1829
    {
1830
        auto card = cards[i];
12,335✔
1831

1832
        if (deck == nullptr && card->m_name == last_name)
12,335✔
1833
        {
1834
            ++num_repeat;
×
1835
        }
1836
        else
1837
        {
1838
            if (num_repeat > 1)
12,335✔
1839
            {
1840
                os << " #" << num_repeat;
×
1841
            }
1842
            if (deck != nullptr)
12,335✔
1843
                os << (first ? "" : ", ") << (deck->card_marks[i] == '!' ? "!" : "") << card->m_name;
1,607✔
1844
            else
1845
                os << (first ? "" : ", ") << card->m_name;
22,359✔
1846
            first = false;
12,335✔
1847
            last_name = card->m_name;
24,670✔
1848
            num_repeat = 1;
1849
        }
1850
    }
1851
    if (num_repeat > 1)
1,269✔
1852
    {
1853
        os << " #" << num_repeat;
×
1854
    }
1855
    os << "\n";
1,269✔
1856
}
1,269✔
1857
void print_score_inline(const FinalResults<long double> score)
1,191✔
1858
{
1859
    // print optimization result details
1860
    switch (optimization_mode)
1,191✔
1861
    {
1862
    case OptimizationMode::raid:
×
1863
    case OptimizationMode::campaign:
×
1864
    case OptimizationMode::brawl:
×
1865
    case OptimizationMode::brawl_defense:
×
1866
    case OptimizationMode::war:
×
1867
    case OptimizationMode::war_defense:
×
1868
#ifndef NQUEST
1869
    case OptimizationMode::quest:
1870
#endif
1871
        std::cout << "(" << score.wins * 100 << "% win";
×
1872
#ifndef NQUEST
1873
        if (optimization_mode == OptimizationMode::quest)
1874
        {
1875
            std::cout << ", " << (score.points - score.wins * tuo::quest.win_score) / (tuo::quest.must_win ? score.wins : 1) / tuo::quest.quest_score * 100 << "% quest";
1876
        }
1877
#endif
1878
        if (show_ci)
×
1879
        {
1880
            std::cout << ", " << score.points_lower_bound << "-" << score.points_upper_bound;
×
1881
        }
1882
        std::cout << ") ";
×
1883
        break;
×
1884
    case OptimizationMode::defense:
×
1885
        std::cout << "(" << score.draws * 100.0 << "% stall) ";
×
1886
        break;
×
1887
    default:
1888
        break;
1889
    }
1890
    std::cout << score.points;
1,191✔
1891
    unsigned min_score = min_possible_score[(size_t)optimization_mode];
1,191✔
1892
    unsigned max_score = max_possible_score[(size_t)optimization_mode];
1,191✔
1893
    if (optimization_mode == OptimizationMode::brawl)
1,191✔
1894
    {
1895
        auto win_points = score.wins ? ((score.points - min_score * (1.0 - score.wins)) / score.wins) : score.points;
×
1896
        std::cout << " [" << win_points << " per win]";
×
1897
    }
1898
    else if (optimization_mode == OptimizationMode::brawl_defense)
1,191✔
1899
    {
1900
        auto opp_win_points = score.losses ? max_score - ((score.points - (max_score - min_score) * (1.0 - score.losses)) / score.losses) : score.points;
×
1901
        std::cout << " [" << opp_win_points << " per opp win]";
×
1902
    }
1903
    else if (optimization_mode == OptimizationMode::war)
1,191✔
1904
    {
1905
        auto win_points = score.wins ? ((score.points - min_score * (1.0 - score.wins)) / score.wins) : score.points;
×
1906
        std::cout << " [" << win_points << " per win]";
×
1907
    }
1908
    else if (optimization_mode == OptimizationMode::war_defense)
1,191✔
1909
    {
1910
        auto opp_win_points = score.losses ? max_score - ((score.points - (max_score - min_score) * (1.0 - score.losses)) / score.losses) : score.points;
×
1911
        std::cout << " [" << opp_win_points << " per opp win]";
×
1912
    }
1913
}
1,191✔
1914
//------------------------------------------------------------------------------
1915
// Calculates and prints individual card value in this deck
1916
void print_sim_card_values(Deck *original_deck, Process &p, unsigned iter) // run_deck == p.your_decks[0]
78✔
1917
{
1918
    if (!print_values && vc_x == 0)
78✔
1919
        return;
78✔
1920
    if (p.your_decks.size() != 1)
×
1921
        return; // only for single deck
1922
    // auto deck = original_deck;
1923
    if (original_deck->strategy == DeckStrategy::random || original_deck->strategy == DeckStrategy::flexible || original_deck->strategy == DeckStrategy::evaluate || original_deck->strategy == DeckStrategy::evaluate_twice)
×
1924
    {
1925
        std::sort(original_deck->cards.begin(), original_deck->cards.end(), [](const Card *a, const Card *b)
×
1926
                  { return a->m_id < b->m_id; });
×
1927
    }
1928
    std::string last_name;
×
1929
    long double score;
×
1930
    Deck *sim_deck = p.your_decks[0];
×
1931
    Deck *your_deck = original_deck->clone(); // save the original deck
×
1932
    auto cards = your_deck->cards;
×
1933
    // sim original/base result
1934
    sim_deck->commander = your_deck->commander;
×
1935
    sim_deck->cards = your_deck->cards;
×
1936
    sim_deck->alpha_dominion = your_deck->alpha_dominion;
×
1937
    EvaluatedResults results = {EvaluatedResults::first_type(p.enemy_decks.size() * p.your_decks.size()), 0};
×
1938
    results = p.evaluate(iter, results);
×
1939
    const FinalResults<long double> fr_base = compute_score(results, p.factors);
×
1940
    long double base = fr_base.points;
×
1941
    std::multimap<long double, std::string> msn;
×
1942
    std::multimap<std::string, long double> mns;
×
1943
    for (unsigned i = 0; i < cards.size(); ++i)
×
1944
    {
1945
        auto card = cards[i];
×
1946
        // if (card->m_name == last_name)
1947
        //{
1948
        // }
1949
        // else
1950
        //{
1951
        last_name = card->m_name;
×
1952
        // sim it
1953
        sim_deck->cards = your_deck->cards;                 // reset cards
×
1954
        sim_deck->cards.erase(sim_deck->cards.begin() + i); // remove to test card
×
1955
        EvaluatedResults results = {EvaluatedResults::first_type(p.enemy_decks.size() * p.your_decks.size()), 0};
×
1956
        results = p.evaluate(iter, results);
×
1957
        const FinalResults<long double> fr = compute_score(results, p.factors);
×
1958
        score = base - fr.points; // subtract from result to get value
×
1959
        // std::cout << card->m_name << " (" << score << "), ";
1960
        msn.insert(std::make_pair(score, card->m_name));
×
1961
        mns.insert(std::make_pair(card->m_name, score));
×
1962
        //}
1963
    }
×
1964
    sim_deck->cards = your_deck->cards; // reset cards
×
1965
    if (vc_x > 0)
×
1966
    {
1967
        // std::cout << "Locked Deck: ";
1968
        ////std::cout << original_deck->cards.size() << " units: ";
1969
        ////print_score_inline(fr_base);
1970
        ////std::cout <<  ": ";
1971
        // std::cout << original_deck->commander->m_name;
1972
        //  print dominions
1973
        // if (original_deck->alpha_dominion)
1974
        //{ std::cout << ", " << original_deck->alpha_dominion->m_name; }
1975

1976
        for (unsigned i = 0; i < cards.size(); ++i)
×
1977
        {
1978
            original_deck->card_marks.erase(i);
×
1979
            if (vc_x >= std::distance(msn.find(mns.find(cards[i]->m_name)->second), msn.end())) // or mns.start()
×
1980
            {
1981
                original_deck->card_marks[i] = '!';
×
1982
            }
1983
        }
1984
    }
1985
    if (print_values)
×
1986
    {
1987
        std::cout << "Value of Cards: ";
×
1988
        for (unsigned i = 0; i < cards.size(); ++i)
×
1989
        {
1990
            std::cout << cards[i]->m_name << " (" << mns.find(cards[i]->m_name)->second << "), ";
×
1991
        }
1992
        std::cout << std::endl;
×
1993
    }
1994
}
×
1995
//------------------------------------------------------------------------------
1996
void print_deck_inline(const unsigned deck_cost, const FinalResults<long double> score, Deck *deck, bool print_locked)
1,191✔
1997
{
1998
    // print units count
1999
    std::cout << deck->cards.size() << " units: ";
1,191✔
2000

2001
    // print deck cost (if fund is enabled)
2002
    if (fund > 0)
1,191✔
2003
    {
2004
        std::cout << "$" << deck_cost << " ";
×
2005
    }
2006
    print_score_inline(score);
1,191✔
2007
    /*
2008
    // print optimization result details
2009
    switch(optimization_mode)
2010
    {
2011
    case OptimizationMode::raid:
2012
    case OptimizationMode::campaign:
2013
    case OptimizationMode::brawl:
2014
    case OptimizationMode::brawl_defense:
2015
    case OptimizationMode::war:
2016
    case OptimizationMode::war_defense:
2017
#ifndef NQUEST
2018
case OptimizationMode::quest:
2019
#endif
2020
std::cout << "(" << score.wins * 100 << "% win";
2021
#ifndef NQUEST
2022
if (optimization_mode == OptimizationMode::quest)
2023
{
2024
std::cout << ", " << (score.points - score.wins * quest.win_score) / (quest.must_win ? score.wins : 1) / quest.quest_score * 100 << "% quest";
2025
}
2026
#endif
2027
if (show_ci)
2028
{
2029
std::cout << ", " << score.points_lower_bound << "-" << score.points_upper_bound;
2030
}
2031
std::cout << ") ";
2032
break;
2033
case OptimizationMode::defense:
2034
std::cout << "(" << score.draws * 100.0 << "% stall) ";
2035
break;
2036
default:
2037
break;
2038
}
2039
std::cout << score.points;
2040
unsigned min_score = min_possible_score[(size_t)optimization_mode];
2041
unsigned max_score = max_possible_score[(size_t)optimization_mode];
2042
if (optimization_mode == OptimizationMode::brawl)
2043
{
2044
auto win_points = score.wins ? ((score.points - min_score * (1.0 - score.wins)) / score.wins) : score.points;
2045
std::cout << " [" << win_points << " per win]";
2046
}
2047
else if (optimization_mode == OptimizationMode::brawl_defense)
2048
{
2049
auto opp_win_points = score.losses ? max_score - ((score.points - (max_score - min_score) * (1.0 - score.losses)) / score.losses) : score.points;
2050
std::cout << " [" << opp_win_points << " per opp win]";
2051
}
2052
else if (optimization_mode == OptimizationMode::war)
2053
{
2054
auto win_points = score.wins ? ((score.points - min_score * (1.0 - score.wins)) / score.wins) : score.points;
2055
std::cout << " [" << win_points << " per win]";
2056
}
2057
else if (optimization_mode == OptimizationMode::war_defense)
2058
{
2059
auto opp_win_points = score.losses ? max_score - ((score.points - (max_score - min_score) * (1.0 - score.losses)) / score.losses) : score.points;
2060
std::cout << " [" << opp_win_points << " per opp win]";
2061
}
2062
*/
2063

2064
    // print commander
2065
    std::cout << ": " << deck->commander->m_name;
1,191✔
2066

2067
    // print dominions
2068
    if (deck->alpha_dominion)
1,191✔
2069
    {
2070
        std::cout << ", " << deck->alpha_dominion->m_name;
1,179✔
2071
    }
2072

2073
    // print deck cards
2074
    if (deck->strategy == DeckStrategy::random || deck->strategy == DeckStrategy::flexible || deck->strategy == DeckStrategy::evaluate || deck->strategy == DeckStrategy::evaluate_twice)
1,191✔
2075
    {
2076
        std::sort(deck->cards.begin(), deck->cards.end(), [](const Card *a, const Card *b)
1,191✔
2077
                  { return a->m_id < b->m_id; });
23,259✔
2078
    }
2079
    std::cout << ", ";
1,191✔
2080
    if (print_locked)
1,191✔
2081
        print_cards_inline(deck->cards, std::cout, deck);
10✔
2082
    else
2083
        print_cards_inline(deck->cards, std::cout);
1,181✔
2084
}
1,191✔
2085
//------------------------------------------------------------------------------
2086
bool is_timeout_reached()
433✔
2087
{
2088
    if (__builtin_expect(maximum_time > 0, false))
433✔
2089
    {
2090
        if ((std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - start_time)).count() > maximum_time * 60 * 60)
×
2091
        {
2092
            std::cout << "Time expired! Current result:" << std::endl;
×
2093
            return true;
×
2094
        }
2095
    }
2096
    return false;
2097
}
2098
std::vector<std::vector<const Card *>> get_candidate_lists(Process &proc)
5✔
2099
{
2100
    std::vector<std::vector<const Card *>> ret_candidates;
5✔
2101
    std::vector<const Card *> commander_candidates;
5✔
2102
    std::vector<const Card *> alpha_dominion_candidates;
5✔
2103
    std::vector<const Card *> card_candidates;
5✔
2104

2105
    // resolve available to player cards
2106
    auto player_assaults_and_structures = proc.cards.player_commanders;
5✔
2107
    player_assaults_and_structures.insert(player_assaults_and_structures.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end());
5✔
2108
    player_assaults_and_structures.insert(player_assaults_and_structures.end(), proc.cards.player_assaults.begin(), proc.cards.player_assaults.end());
5✔
2109
    for (auto it = player_assaults_and_structures.begin(); it != player_assaults_and_structures.end(); ++it)
210,550✔
2110
    {
2111
        const Card *card = *it;
210,545✔
2112
        // skip illegal
2113
        if ((card->m_category != CardCategory::dominion_alpha) && (card->m_category != CardCategory::normal))
210,545✔
2114
        {
2115
            continue;
210,370✔
2116
        }
2117

2118
        // skip dominions when their climbing is disabled
2119
        if ((card->m_category == CardCategory::dominion_alpha) && (!use_owned_dominions))
210,545✔
2120
        {
2121
            continue;
×
2122
        }
2123

2124
        // try to skip a card unless it's allowed
2125
        if (!allowed_candidates.count(card->m_id))
210,545✔
2126
        {
2127
            // skip disallowed always
2128
            if (disallowed_candidates.count(card->m_id))
210,545✔
2129
            {
2130
                continue;
×
2131
            }
2132

2133
            // handle dominions
2134
            if (card->m_category == CardCategory::dominion_alpha)
210,545✔
2135
            {
2136
                // skip non-top-level dominions anyway
2137
                // (will check it later and downgrade if necessary according to amount of material (shards))
2138
                /*if (!card->is_top_level_card())
2139
                  { continue; }
2140

2141
                // skip basic dominions
2142
                if ((card->m_id == 50001) || (card->m_id == 50002))
2143
                { continue; }*/
2144
            }
2145

2146
            // handle normal cards
2147
            else
2148
            {
2149
                // skip non-top-level cards (adjust_deck() will try to downgrade them if necessary)
2150
                bool use_top_level = (card->m_type == CardType::commander) ? use_top_level_commander : use_top_level_card;
208,755✔
2151
                if (!card->is_top_level_card() and (fund || use_top_level || !owned_cards[card->m_id]))
208,755✔
2152
                {
2153
                    continue;
173,440✔
2154
                }
2155

2156
                // skip lowest fusion levels
2157
                unsigned use_fused_level = (card->m_type == CardType::commander) ? use_fused_commander_level : use_fused_card_level;
35,315✔
2158
                if (card->m_fusion_level < use_fused_level)
35,315✔
2159
                {
2160
                    continue;
×
2161
                }
2162
            }
2163
        }
2164

2165
        if (use_owned_cards && card->m_category == CardCategory::dominion_alpha && !owned_cards[card->m_id])
37,105✔
2166
        {
2167
            if (use_maxed_dominions && card->m_used_for_cards.size() == 0)
1,775✔
2168
            {
2169
            }
2170
            else
2171
            {
2172
                continue;
1,775✔
2173
            }
2174
        }
2175
        // skip unavailable cards anyway when ownedcards is used
2176
        if (use_owned_cards && !(card->m_category == CardCategory::dominion_alpha) && !is_owned_or_can_be_fused(card))
35,330✔
2177
        {
2178
            continue;
35,155✔
2179
        }
2180
        // mono
2181
        if (!factions.empty() && (std::find(factions.begin(), factions.end(), card->m_faction) == factions.end()) != invert_factions) // XOR
175✔
2182
        {
2183
            continue;
×
2184
        }
2185

2186
        // enqueue candidate according to category & type
2187
        if (card->m_type == CardType::commander)
175✔
2188
        {
2189
            commander_candidates.emplace_back(card);
45✔
2190
        }
2191
        else if (card->m_category == CardCategory::dominion_alpha)
130✔
2192
        {
2193
            alpha_dominion_candidates.emplace_back(card);
15✔
2194
        }
2195
        else if (card->m_category == CardCategory::normal)
115✔
2196
        {
2197
            bool contains{false};
115✔
2198
            if (!skills.empty())
115✔
2199
            {
2200
                for (Skill::Skill skill_id : skills)
×
2201
                {
2202
                    if (card->m_skill_value[skill_id])
×
2203
                    {
2204
                        contains = true;
2205
                        break;
2206
                    }
2207
                }
2208
                if (!contains != invert_skills)
×
2209
                    continue; // XOR
×
2210
            }
2211
            if (only_recent && !((it + (player_assaults_and_structures.size() * recent_percent / 100)) > player_assaults_and_structures.end()))
115✔
2212
                continue;                                                                                                                      // latest %
×
2213
            if (prefered_recent && it + (player_assaults_and_structures.size() * recent_percent) / 100 > player_assaults_and_structures.end()) // latest %
115✔
2214
            {
2215
                for (unsigned k = 0; k < prefered_factor; ++k)
×
2216
                    card_candidates.emplace_back(card);
×
2217
            }
2218
            for (Skill::Skill skill_id : prefered_skills)
115✔
2219
            {
2220
                if (card->m_skill_value[skill_id])
×
2221
                {
2222
                    for (unsigned k = 0; k < prefered_factor; ++k)
×
2223
                        card_candidates.emplace_back(card);
×
2224
                }
2225
            }
2226

2227
            card_candidates.emplace_back(card);
115✔
2228
        }
2229
    }
2230
    card_candidates.emplace_back(nullptr);
5✔
2231

2232
    ret_candidates.emplace_back(commander_candidates);
5✔
2233
    ret_candidates.emplace_back(alpha_dominion_candidates);
5✔
2234
    ret_candidates.emplace_back(card_candidates);
5✔
2235
    return ret_candidates;
5✔
2236
}
5✔
2237
//------------------------------------------------------------------------------
2238
enum Operation
2239
{
2240
    noop,
2241
    simulate,
2242
    climb,
2243
    climb_forts,
2244
    anneal,
2245
    genetic,
2246
    beam,
2247
    reorder,
2248
    debug,
2249
    debuguntil,
2250
};
2251
//------------------------------------------------------------------------------
2252
extern void (*skill_table[Skill::num_skills])(Field *, CardStatus *src_status, const SkillSpec &);
2253
void print_available_effects()
×
2254
{
2255
    std::cout << "Available effects besides activation skills:\n"
×
2256
                 "  Bloodlust X\n"
2257
                 "  Brigade\n"
2258
                 "  Counterflux\n"
2259
                 "  Divert\n"
2260
                 "  Enduring-Rage\n"
2261
                 "  Fortification\n"
2262
                 "  Heroism\n"
2263
                 "  Zealots-Preservation\n"
2264
                 "  Metamorphosis\n"
2265
                 "  Megamorphosis\n"
2266
                 "  Revenge X\n"
2267
                 "  Turning-Tides\n"
2268
                 "  Virulence\n"
2269
                 "  Halted-Orders\n"
2270
                 "  Devour X\n"
2271
                 "  Critical-Reach\n"
2272
                 "  Temporal-Backlash\n"
2273
                 "  Furiosity\n"
2274
                 "  Oath-Of-Loyalty\n"
2275
                 "  Blood-Vengeance\n"
2276
                 "  Cold-Sleep\n"
2277
                 "  Iron-Will\n"
2278
                 "  Unity\n"
2279
                 "  Devotion\n"
2280
                 "  Crackdown\n"
2281
                 "  SuperHeroism\n";
×
2282
}
×
2283
bool check_input_amount(int argc, const char **argv, int argIndex, int number)
371✔
2284
{
2285
    if (argc <= argIndex + number)
371✔
2286
    {
2287
        std::cerr << argv[argIndex] << " needs " << number << " paramters" << std::endl;
×
2288
        return true;
×
2289
    }
2290
    return false;
2291
}
2292
void input_error(std::string msg)
×
2293
{
2294
    std::cerr << msg << std::endl;
×
2295
    exit(EXIT_FAILURE);
×
2296
}
2297
void usage(int argc, const char **argv)
×
2298
{
2299
    std::cout << "Tyrant Unleashed Optimizer (TUO) " << TYRANT_OPTIMIZER_VERSION << "\n"
×
2300
                                                                                    "usage: "
2301
              << argv[0] << " Your_Deck Enemy_Deck [Flags] [Operations]\n"
2302
                            "\n"
2303
                            "Your_Deck:\n"
2304
                            "  the name/hash/cards of a custom deck.\n"
2305
                            "\n"
2306
                            "Enemy_Deck:\n"
2307
                            "  semicolon separated list of defense decks, syntax:\n"
2308
                            "  deck1[:factor1];deck2[:factor2];...\n"
2309
                            "  where deck is the name/hash/cards of a mission, raid, quest or custom deck, and factor is optional. The default factor is 1.\n"
2310
                            "  example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n"
2311
                            "\n"
2312
                            "Flags:\n"
2313
                            "  -e \"<effect>\": set the battleground effect; you may use -e multiple times.\n"
2314
                            "  -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n"
2315
                            "  -s: use surge (default is fight).\n"
2316
                            "  -t <num>: set the number of threads, default is 4.\n"
2317
                            "  win:     simulate/optimize for win rate. default for non-raids.\n"
2318
                            "  defense: simulate/optimize for win rate + stall rate. can be used for defending deck or win rate oriented raid simulations.\n"
2319
                            "  raid:    simulate/optimize for average raid damage (ARD). default for raids.\n"
2320
                            "Flags for climb:\n"
2321
                            "  -c: don't try to optimize the commander.\n"
2322
                            "  -L <min> <max>: restrict deck size between <min> and <max>.\n"
2323
                            "  -o: restrict to the owned cards listed in \"data/ownedcards.txt\".\n"
2324
                            "  -o=<filename>: restrict to the owned cards listed in <filename>.\n"
2325
                            "  fund <num>: invest <num> SP to upgrade cards.\n"
2326
                            "  target <num>: stop as soon as the score reaches <num>.\n"
2327
                            "\n"
2328
                            "Operations:\n"
2329
                            "  sim <num>: simulate <num> battles to evaluate a deck.\n"
2330
                            "  climb <num>: perform hill-climbing starting from the given attack deck, using up to <num> battles to evaluate a deck.\n"
2331
                            "  reorder <num>: optimize the order for given attack deck, using up to <num> battles to evaluate an order.\n"
2332
#ifndef NDEBUG
2333
                            "  debug: testing purpose only. very verbose output. only one battle.\n"
2334
                            "  debuguntil <min> <max>: testing purpose only. fight until the last fight results in range [<min>, <max>]. recommend to redirect std::coutput.\n"
×
2335
#endif
2336
        ;
2337
}
×
2338

2339
bool parse_bge(
60✔
2340
    std::string bge_name,
2341
    unsigned player,
2342
    const std::unordered_map<std::string, std::string> &bge_aliases,
2343
    std::array<signed short, PassiveBGE::num_passive_bges> &your_bg_effects,
2344
    std::array<signed short, PassiveBGE::num_passive_bges> &enemy_bg_effects,
2345
    std::vector<SkillSpec> &your_bg_skills,
2346
    std::vector<SkillSpec> &enemy_bg_skills,
2347
    std::unordered_set<std::string> used_bge_aliases)
2348
{
2349
    // skip empty
2350
    tuo::trim(bge_name);
60✔
2351
    if (bge_name.empty())
60✔
2352
    {
2353
        return true;
2354
    }
2355

2356
    // is effect combined?
2357
    if (bge_name.find_first_of(";|") != std::string::npos)
36✔
2358
    {
2359
        std::vector<std::string> bges;
×
2360
        boost::split(bges, bge_name, boost::is_any_of(";|"));
×
2361
        for (auto &&next_bge : bges)
×
2362
        {
2363
            if (!parse_bge(next_bge, player, bge_aliases, your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills, used_bge_aliases))
×
2364
            {
2365
                return false;
×
2366
            }
2367
        }
2368
        return true;
2369
    }
×
2370

2371
    // try to resolve bge name as alias
2372
    std::string simple_bge_name = simplify_name(bge_name);
36✔
2373
    const auto bge_alias_itr = bge_aliases.find(simple_bge_name);
36✔
2374
    if (bge_alias_itr != bge_aliases.end())
36✔
2375
    {
2376
        if (!used_bge_aliases.insert(simple_bge_name).second)
1✔
2377
        {
2378
            throw std::runtime_error("BGE alias: " + bge_name + ": Circular reference");
×
2379
        }
2380
        return parse_bge(bge_alias_itr->second, player, bge_aliases, your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills, used_bge_aliases);
3✔
2381
    }
2382

2383
    // parse as passive or skill based BGE
2384
    std::vector<std::string> tokens, skill_name_list;
35✔
2385
    boost::split(tokens, bge_name, boost::is_any_of(" "));
35✔
2386
    boost::split(skill_name_list, tokens[0], boost::is_any_of("+"));
35✔
2387
    try
35✔
2388
    {
2389
        for (auto &&skill_name : skill_name_list)
70✔
2390
        {
2391
            PassiveBGE::PassiveBGE passive_bge_id = passive_bge_name_to_id(skill_name);
35✔
2392
            Skill::Skill skill_id = skill_name_to_id(skill_name);
35✔
2393
            if (passive_bge_id != PassiveBGE::no_bge)
35✔
2394
            {
2395
                // map bge id to its value (if present otherwise zero)
2396
                signed short bge_value = (tokens.size() > 1) ? boost::lexical_cast<signed short>(tokens[1]) : 1;
34✔
2397
                if (!bge_value)
6✔
2398
                    throw std::runtime_error("BGE " + skill_name + ": zero value means no BGE");
×
2399
                if ((player == 0) or (player == 2))
34✔
2400
                    your_bg_effects[passive_bge_id] = bge_value;
34✔
2401
                if ((player == 1) or (player == 2))
34✔
2402
                    enemy_bg_effects[passive_bge_id] = bge_value;
34✔
2403
            }
2404
            else if (skill_table[skill_id] != nullptr)
1✔
2405
            {
2406
                unsigned skill_index = 1;
1✔
2407
                // activation BG skill
2408
                SkillSpec bg_skill{
1✔
2409
                    skill_id,
2410
                    0,
2411
                    allfactions,
2412
                    0,
2413
                    0,
2414
                    Skill::no_skill,
2415
                    Skill::no_skill,
2416
                    false,
2417
                    0,
2418
                };
1✔
2419

2420
                // skill [ ALL | N ] ...
2421
                if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "all")
2✔
2422
                {
2423
                    bg_skill.all = true;
1✔
2424
                    skill_index += 1;
1✔
2425
                }
2426
                else if (skill_index + 1 < tokens.size() && isdigit(*tokens[skill_index].c_str()))
×
2427
                {
2428
                    bg_skill.n = boost::lexical_cast<unsigned>(tokens[skill_index]);
×
2429
                    skill_index += 1;
×
2430
                }
2431

2432
                // skill n [ FACTION ] ...
2433
                if ((skill_index + 1) < tokens.size())
1✔
2434
                {
2435
                    for (auto f = allfactions + 1; f < num_factions; ++f)
7✔
2436
                    {
2437
                        if (boost::to_lower_copy(tokens[skill_index]) == faction_names[f])
6✔
2438
                        {
2439
                            bg_skill.y = static_cast<Faction>(f);
×
2440
                            skill_index += 1;
×
2441
                            break;
×
2442
                        }
2443
                    }
2444
                }
2445

2446
                if (skill_index < tokens.size())
1✔
2447
                {
2448
                    bg_skill.s = skill_name_to_id(tokens[skill_index]);
1✔
2449
                    if (bg_skill.s != Skill::no_skill)
1✔
2450
                    {
2451
                        skill_index += 1;
1✔
2452
                        if (skill_index < tokens.size() && (boost::to_lower_copy(tokens[skill_index]) == "to" || boost::to_lower_copy(tokens[skill_index]) == "into"))
3✔
2453
                        {
2454
                            skill_index += 1;
×
2455
                        }
2456
                        if (skill_index < tokens.size())
1✔
2457
                        {
2458
                            bg_skill.s2 = skill_name_to_id(tokens[skill_index]);
1✔
2459
                            if (bg_skill.s2 != Skill::no_skill)
1✔
2460
                            {
2461
                                skill_index += 1;
×
2462
                            }
2463
                        }
2464
                    }
2465
                }
2466
                if (skill_index < tokens.size())
1✔
2467
                {
2468
                    if (bg_skill.id == Skill::jam || bg_skill.id == Skill::overload)
1✔
2469
                    {
2470
                        bg_skill.n = boost::lexical_cast<unsigned>(tokens[skill_index]);
×
2471
                    }
2472
                    else
2473
                    {
2474
                        bg_skill.x = boost::lexical_cast<unsigned>(tokens[skill_index]);
1✔
2475
                    }
2476
                }
2477
                switch (player)
1✔
2478
                {
2479
                case 0:
×
2480
                    your_bg_skills.push_back(bg_skill);
×
2481
                    break;
2482
                case 1:
×
2483
                    enemy_bg_skills.push_back(bg_skill);
×
2484
                    break;
2485
                case 2:
1✔
2486
                    your_bg_skills.push_back(bg_skill);
1✔
2487
                    enemy_bg_skills.push_back(bg_skill);
1✔
2488
                    break;
2489
                default:
×
2490
                    throw std::runtime_error("Bad player number: " + boost::lexical_cast<std::string>(player));
×
2491
                }
2492
            }
2493
            else
2494
            {
2495
                std::cerr << "Error: unrecognized effect \"" << bge_name << "\".\n";
×
2496
                std::cout << "Unrecognized effect \"" << bge_name << "\".\n";
×
2497
                print_available_effects();
×
2498
                return false;
35✔
2499
            }
2500
        }
2501
    }
2502
    catch (const boost::bad_lexical_cast &e)
×
2503
    {
2504
        throw std::runtime_error("Expect a number in effect \"" + bge_name + "\"");
×
2505
    }
×
2506
    return true;
2507
}
71✔
2508

2509
DeckResults run(int argc, const char **argv)
78✔
2510
{
2511
    start_time = std::chrono::system_clock::now();
78✔
2512
    DeckResults fr;
78✔
2513
    opt_num_threads = 4;
78✔
2514
    DeckStrategy::DeckStrategy opt_your_strategy(DeckStrategy::random);
78✔
2515
    DeckStrategy::DeckStrategy opt_enemy_strategy(DeckStrategy::random);
78✔
2516
    std::string opt_forts, opt_enemy_forts;
78✔
2517
    std::string opt_doms, opt_enemy_doms;
78✔
2518
    std::string opt_hand, opt_enemy_hand;
78✔
2519
    std::string opt_vip;
78✔
2520
    std::string opt_allow_candidates;
78✔
2521
    std::string opt_disallow_candidates;
78✔
2522
    std::string opt_disallow_recipes;
78✔
2523
#ifndef NQUEST
2524
    std::string opt_quest;
2525
#endif
2526
    std::string opt_target_score;
78✔
2527
    std::vector<std::string> fn_suffix_list{
78✔
2528
        "",
2529
    };
156✔
2530
    std::string prefix = "";
156✔
2531
    std::vector<std::string> opt_owned_cards_str_list;
78✔
2532
    bool opt_do_optimization(false);
78✔
2533
    bool opt_multi_optimization(false);
78✔
2534
    bool opt_do_reorder(false);
78✔
2535
    bool opt_keep_commander{false};
78✔
2536
    std::vector<std::tuple<unsigned, unsigned, Operation>> opt_todo;
78✔
2537
    std::vector<std::string> opt_effects[3]; // 0-you; 1-enemy; 2-global
546✔
2538
    std::array<signed short, PassiveBGE::num_passive_bges> opt_bg_effects[2];
2539
    std::vector<SkillSpec> opt_bg_skills[2];
390✔
2540

2541
    std::string your_deck_list{argv[1]};
78✔
2542
    std::string enemy_deck_list{argv[2]};
78✔
2543

2544
    for (int argIndex = 3; argIndex < argc; ++argIndex)
470✔
2545
    {
2546
        //bypass MSVS issue: Nesting of code blocks exceeds the limit of 128 nesting levels
2547
        bool tokenParsed = true;
392✔
2548
        if (strcmp(argv[argIndex], "deck") == 0)
392✔
2549
        {
2550
            your_deck_list = std::string(argv[argIndex + 1]);
×
2551
            argIndex += 1;
×
2552
        }
2553
        else if (strcmp(argv[argIndex], "enemy:deck") == 0)
392✔
2554
        {
2555
            enemy_deck_list = std::string(argv[argIndex + 1]);
×
2556
            argIndex += 1;
×
2557
        }
2558
        // Codec
2559
        else if (strcmp(argv[argIndex], "ext_b64") == 0)
392✔
2560
        {
2561
            hash_to_ids = hash_to_ids_ext_b64;
×
2562
            encode_deck = encode_deck_ext_b64;
×
2563
        }
2564
        else if (strcmp(argv[argIndex], "wmt_b64") == 0)
392✔
2565
        {
2566
            hash_to_ids = hash_to_ids_wmt_b64;
×
2567
            encode_deck = encode_deck_wmt_b64;
×
2568
        }
2569
        else if (strcmp(argv[argIndex], "ddd_b64") == 0)
392✔
2570
        {
2571
            hash_to_ids = hash_to_ids_ddd_b64;
×
2572
            encode_deck = encode_deck_ddd_b64;
×
2573
        }
2574
        else if (strcmp(argv[argIndex], "ml") == 0)
392✔
2575
        {
2576
            use_ml = true;
1✔
2577
            use_db_write = false;
1✔
2578
        }
2579
        else if (strcmp(argv[argIndex], "noop") == 0)
391✔
2580
        {
2581
        }
2582
        else if (strcmp(argv[argIndex], "no-ml") == 0)
391✔
2583
        {
2584
            use_ml = false;
×
2585
        }
2586
        else if (strcmp(argv[argIndex], "only-ml") == 0)
391✔
2587
        {
2588
            use_ml = true;
×
2589
            use_only_ml = true;
×
2590
            use_db_write = false;
×
2591
        }
2592
        else if (strcmp(argv[argIndex], "ml-precision") == 0)
391✔
2593
        {
2594
            if (check_input_amount(argc, argv, argIndex, 1))
×
2595
                exit(1);
×
2596
            ml_precision = std::stof(argv[argIndex + 1]);
×
2597
            argIndex += 1;
×
2598
        }
2599
        else if (strcmp(argv[argIndex], "no-db-write") == 0)
391✔
2600
        {
2601
            use_db_write = false;
×
2602
        }
2603
        else if (strcmp(argv[argIndex], "no-db-load") == 0)
391✔
2604
        {
2605
            use_db_load = false;
2✔
2606
        }
2607
        else if (strcmp(argv[argIndex], "no-db") == 0)
389✔
2608
        {
2609
            use_db_load = false;
16✔
2610
            use_db_write = false;
16✔
2611
        }
2612
        else if (strcmp(argv[argIndex], "db") == 0)
373✔
2613
        {
2614
            use_db_load = true;
2✔
2615
            use_db_write = true;
2✔
2616
        }
2617
        else if (strcmp(argv[argIndex], "strict-db") == 0)
371✔
2618
        {
2619
            use_strict_db = true;
×
2620
            use_db_load = true;
×
2621
            use_db_write = true;
×
2622
        }
2623
        else if (strcmp(argv[argIndex], "no-strict-db") == 0)
371✔
2624
        {
2625
            use_strict_db = false;
×
2626
        }
2627
        else if (strcmp(argv[argIndex], "db-limit") == 0)
371✔
2628
        {
2629
            if (check_input_amount(argc, argv, argIndex, 1))
×
2630
                exit(1);
×
2631
            db_limit = std::stoi(argv[argIndex + 1]);
×
2632
            argIndex += 1;
×
2633
        }
2634

2635
        // Base Game Mode
2636
        else if (strcmp(argv[argIndex], "fight") == 0)
371✔
2637
        {
2638
            gamemode = fight;
×
2639
        }
2640
        else if (strcmp(argv[argIndex], "-s") == 0 || strcmp(argv[argIndex], "surge") == 0)
371✔
2641
        {
2642
            gamemode = surge;
×
2643
        }
2644
        // Base Scoring Mode
2645
        else if (strcmp(argv[argIndex], "win") == 0)
371✔
2646
        {
2647
            optimization_mode = OptimizationMode::winrate;
×
2648
        }
2649
        else if (strcmp(argv[argIndex], "defense") == 0)
371✔
2650
        {
2651
            optimization_mode = OptimizationMode::defense;
×
2652
        }
2653
        else if (strcmp(argv[argIndex], "raid") == 0)
371✔
2654
        {
2655
            optimization_mode = OptimizationMode::raid;
×
2656
        }
2657
        // Mode Package
2658
        else if (strcmp(argv[argIndex], "campaign") == 0)
371✔
2659
        {
2660
            gamemode = surge;
×
2661
            optimization_mode = OptimizationMode::campaign;
×
2662
        }
2663
        else if (strcmp(argv[argIndex], "pvp") == 0)
371✔
2664
        {
2665
            gamemode = fight;
×
2666
            optimization_mode = OptimizationMode::winrate;
×
2667
        }
2668
        else if (strcmp(argv[argIndex], "pvp-defense") == 0)
371✔
2669
        {
2670
            gamemode = surge;
×
2671
            optimization_mode = OptimizationMode::defense;
×
2672
        }
2673
        else if (strcmp(argv[argIndex], "brawl") == 0)
371✔
2674
        {
2675
            gamemode = surge;
×
2676
            optimization_mode = OptimizationMode::brawl;
×
2677
        }
2678
        else if (strcmp(argv[argIndex], "brawl-defense") == 0)
371✔
2679
        {
2680
            gamemode = fight;
×
2681
            optimization_mode = OptimizationMode::brawl_defense;
×
2682
        }
2683
        else if (strcmp(argv[argIndex], "gw") == 0)
371✔
2684
        {
2685
            gamemode = surge;
×
2686
            optimization_mode = OptimizationMode::war;
×
2687
        }
2688
        else if (strcmp(argv[argIndex], "gw-defense") == 0)
371✔
2689
        {
2690
            gamemode = fight;
×
2691
            optimization_mode = OptimizationMode::war_defense;
×
2692
        }
2693
        // Others
2694
        else if (strcmp(argv[argIndex], "keep-commander") == 0 || strcmp(argv[argIndex], "-c") == 0)
371✔
2695
        {
2696
            opt_keep_commander = true;
2697
        }
2698
        else if (strcmp(argv[argIndex], "mono") == 0 || strcmp(argv[argIndex], "-m") == 0 || strcmp(argv[argIndex], "factions") == 0 || strcmp(argv[argIndex], "-f") == 0)
371✔
2699
        {
2700
            if (check_input_amount(argc, argv, argIndex, 1))
×
2701
                exit(1);
×
2702
            if (strcmp(argv[argIndex + 1], "") != 0)
×
2703
            {
2704
                factions.push_back(faction_name_to_id(argv[argIndex + 1]));
×
2705
            }
2706
            argIndex += 1;
×
2707
        }
2708
        else if (strcmp(argv[argIndex], "no-mono") == 0 || strcmp(argv[argIndex], "no-factions") == 0)
371✔
2709
        {
2710
            if (check_input_amount(argc, argv, argIndex, 1))
×
2711
                exit(1);
×
2712
            factions.push_back(faction_name_to_id(argv[argIndex + 1]));
×
2713
            invert_factions = true;
×
2714
            argIndex += 1;
×
2715
        }
2716
        else if (strcmp(argv[argIndex], "strategy") == 0 || strcmp(argv[argIndex], "skill") == 0)
371✔
2717
        {
2718
            if (check_input_amount(argc, argv, argIndex, 1))
×
2719
                exit(1);
×
2720
            if (strcmp(argv[argIndex + 1], "") != 0)
×
2721
            {
2722
                if (strcmp(argv[argIndex + 1], "recent") == 0)
×
2723
                {
2724
                    only_recent = true;
×
2725
                }
2726
                else
2727
                {
2728
                    skills.push_back(skill_name_to_id(argv[argIndex + 1]));
×
2729
                }
2730
            }
2731
            argIndex += 1;
×
2732
        }
2733
        else if (strcmp(argv[argIndex], "no-strategy") == 0 || strcmp(argv[argIndex], "no-skill") == 0)
371✔
2734
        {
2735
            if (check_input_amount(argc, argv, argIndex, 1))
×
2736
                exit(1);
×
2737
            if (strcmp(argv[argIndex + 1], "") != 0)
×
2738
            {
2739
                skills.push_back(skill_name_to_id(argv[argIndex + 1]));
×
2740
                invert_skills = true;
×
2741
            }
2742
            argIndex += 1;
×
2743
        }
2744
        else if (strcmp(argv[argIndex], "prefered-strategy") == 0 || strcmp(argv[argIndex], "prefered-skill") == 0)
371✔
2745
        {
2746
            if (check_input_amount(argc, argv, argIndex, 1))
×
2747
                exit(1);
×
2748
            if (strcmp(argv[argIndex + 1], "") != 0)
×
2749
            {
2750
                if (strcmp(argv[argIndex + 1], "recent") == 0)
×
2751
                {
2752
                    prefered_recent = true;
×
2753
                }
2754
                else
2755
                {
2756
                    prefered_skills.push_back(skill_name_to_id(argv[argIndex + 1]));
×
2757
                }
2758
            }
2759
            argIndex += 1;
×
2760
        }
2761
        else if (strcmp(argv[argIndex], "prefered-factor") == 0)
371✔
2762
        {
2763
            if (check_input_amount(argc, argv, argIndex, 1))
×
2764
                exit(1);
×
2765
            prefered_factor = std::stoi(argv[argIndex + 1]);
×
2766
            argIndex += 1;
×
2767
        }
2768
        else if (strcmp(argv[argIndex], "recent-percent") == 0)
371✔
2769
        {
2770
            if (check_input_amount(argc, argv, argIndex, 1))
×
2771
                exit(1);
×
2772
            recent_percent = std::stoi(argv[argIndex + 1]);
×
2773
            argIndex += 1;
×
2774
        }
2775
        else if (strcmp(argv[argIndex], "effect") == 0 || strcmp(argv[argIndex], "-e") == 0)
371✔
2776
        {
2777
            if (check_input_amount(argc, argv, argIndex, 1))
59✔
2778
                exit(1);
×
2779
            opt_effects[2].push_back(argv[argIndex + 1]);
118✔
2780
            argIndex += 1;
59✔
2781
        }
2782
        else if (strcmp(argv[argIndex], "ye") == 0 || strcmp(argv[argIndex], "yeffect") == 0)
312✔
2783
        {
2784
            if (check_input_amount(argc, argv, argIndex, 1))
×
2785
                exit(1);
×
2786
            opt_effects[0].push_back(argv[argIndex + 1]);
×
2787
            argIndex += 1;
×
2788
        }
2789
        else if (strcmp(argv[argIndex], "ee") == 0 || strcmp(argv[argIndex], "eeffect") == 0)
312✔
2790
        {
2791
            if (check_input_amount(argc, argv, argIndex, 1))
×
2792
                exit(1);
×
2793
            opt_effects[1].push_back(argv[argIndex + 1]);
×
2794
            argIndex += 1;
×
2795
        }
2796
        else if (strcmp(argv[argIndex], "freeze") == 0 || strcmp(argv[argIndex], "-F") == 0)
312✔
2797
        {
2798
            if (check_input_amount(argc, argv, argIndex, 1))
×
2799
                exit(1);
×
2800
            opt_freezed_cards = atoi(argv[argIndex + 1]);
×
2801
            argIndex += 1;
×
2802
        }
2803
        else if (strcmp(argv[argIndex], "-L") == 0)
312✔
2804
        {
2805
            if (check_input_amount(argc, argv, argIndex, 2))
×
2806
                exit(1);
×
2807
            min_deck_len = atoi(argv[argIndex + 1]);
×
2808
            max_deck_len = atoi(argv[argIndex + 2]);
×
2809
            argIndex += 2;
×
2810
        }
2811
        else if (strcmp(argv[argIndex], "-o-") == 0)
312✔
2812
        {
2813
            use_owned_cards = false;
×
2814
        }
2815
        else if (strcmp(argv[argIndex], "-o") == 0)
312✔
2816
        {
2817
            opt_owned_cards_str_list.push_back(prefix + "data/ownedcards.txt");
×
2818
            use_owned_cards = true;
×
2819
        }
2820
        else if (strncmp(argv[argIndex], "-o=", 3) == 0)
312✔
2821
        {
2822
            std::string fname{prefix};
×
2823
            fname += (argv[argIndex] + 3);
×
2824
            opt_owned_cards_str_list.push_back(fname);
×
2825
            use_owned_cards = true;
×
2826
        }
×
2827
        else if (strncmp(argv[argIndex], "_", 1) == 0)
312✔
2828
        {
2829
            fn_suffix_list.push_back(argv[argIndex]);
×
2830
        }
2831
        else if (strcmp(argv[argIndex], "prefix") == 0)
312✔
2832
        {
2833
            if (check_input_amount(argc, argv, argIndex, 1))
78✔
2834
                exit(1);
×
2835
            prefix = argv[argIndex + 1];
78✔
2836
            argIndex += 1;
78✔
2837
        }
2838
        else if (strcmp(argv[argIndex], "fund") == 0)
234✔
2839
        {
2840
            if (check_input_amount(argc, argv, argIndex, 1))
×
2841
                exit(1);
×
2842
            fund = atoi(argv[argIndex + 1]);
×
2843
            argIndex += 1;
×
2844
        }
2845
        else if (strcmp(argv[argIndex], "dom-none") == 0)
234✔
2846
        {
2847
            use_owned_dominions = false;
×
2848
            use_maxed_dominions = false;
×
2849
        }
2850
        else if (strcmp(argv[argIndex], "dom+") == 0 || strcmp(argv[argIndex], "dominion+") == 0 || strcmp(argv[argIndex], "dom-owned") == 0)
234✔
2851
        {
2852
            use_owned_dominions = true;
×
2853
            use_maxed_dominions = false;
×
2854
        }
2855
        else if (strcmp(argv[argIndex], "dom-") == 0 || strcmp(argv[argIndex], "dominion-") == 0 || strcmp(argv[argIndex], "dom-maxed") == 0)
234✔
2856
        {
2857
            use_owned_dominions = true;
×
2858
            use_maxed_dominions = true;
×
2859
        }
2860
        else if (strcmp(argv[argIndex], "random") == 0)
234✔
2861
        {
2862
            opt_your_strategy = DeckStrategy::random;
2863
        }
2864
        else if (strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0)
234✔
2865
        {
2866
            opt_your_strategy = DeckStrategy::ordered;
2867
        }
2868
        else if (strcmp(argv[argIndex], "flexible") == 0 || strcmp(argv[argIndex], "flex") == 0)
234✔
2869
        {
2870
            opt_your_strategy = DeckStrategy::flexible;
2871
        }
2872
        else if (strcmp(argv[argIndex], "flexible-iter") == 0)
234✔
2873
        {
2874
            if (check_input_amount(argc, argv, argIndex, 1))
×
2875
                exit(1);
×
2876
            flexible_iter = atoi(argv[argIndex + 1]);
×
2877
            argIndex += 1;
×
2878
        }
2879
        else if (strcmp(argv[argIndex], "flexible-turn") == 0)
234✔
2880
        {
2881
            if (check_input_amount(argc, argv, argIndex, 1))
×
2882
                exit(1);
×
2883
            flexible_turn = atoi(argv[argIndex + 1]);
×
2884
            argIndex += 1;
×
2885
        }
2886
        else if (strcmp(argv[argIndex], "evaluate") == 0 || strcmp(argv[argIndex], "eval") == 0)
234✔
2887
        {
2888
            opt_your_strategy = DeckStrategy::evaluate;
2889
        }
2890
        else if (strcmp(argv[argIndex], "evaluate2") == 0 || strcmp(argv[argIndex], "eval2") == 0)
234✔
2891
        {
2892
            opt_your_strategy = DeckStrategy::evaluate_twice;
2893
        }
2894
        else if (strcmp(argv[argIndex], "eval-iter") == 0)
234✔
2895
        {
2896
            if (check_input_amount(argc, argv, argIndex, 1))
×
2897
                exit(1);
×
2898
            eval_iter = atoi(argv[argIndex + 1]);
×
2899
            argIndex += 1;
×
2900
        }
2901
        else if (strcmp(argv[argIndex], "eval-turn") == 0)
234✔
2902
        {
2903
            if (check_input_amount(argc, argv, argIndex, 1))
×
2904
                exit(1);
×
2905
            eval_turn = atoi(argv[argIndex + 1]);
×
2906
            argIndex += 1;
×
2907
        }
2908
        else if (strcmp(argv[argIndex], "exact-ordered") == 0)
234✔
2909
        {
2910
            opt_your_strategy = DeckStrategy::exact_ordered;
2911
        }
2912
        else if (strcmp(argv[argIndex], "enemy:evaluate") == 0)
234✔
2913
        {
2914
            opt_enemy_strategy = DeckStrategy::evaluate;
2915
        }
2916
        else if (strcmp(argv[argIndex], "enemy:evaluate2") == 0)
234✔
2917
        {
2918
            opt_enemy_strategy = DeckStrategy::evaluate_twice;
2919
        }
2920
        else if (strcmp(argv[argIndex], "enemy:flexible") == 0)
234✔
2921
        {
2922
            opt_enemy_strategy = DeckStrategy::flexible;
2923
        }
2924
        else if (strcmp(argv[argIndex], "enemy:ordered") == 0)
234✔
2925
        {
2926
            opt_enemy_strategy = DeckStrategy::ordered;
2927
        }
2928
        else if (strcmp(argv[argIndex], "enemy:flexible") == 0)
234✔
2929
        {
2930
            opt_enemy_strategy = DeckStrategy::flexible;
2931
        }
2932
        else if (strcmp(argv[argIndex], "enemy:exact-ordered") == 0)
234✔
2933
        {
2934
            opt_enemy_strategy = DeckStrategy::exact_ordered;
2935
        }
2936
        else if (strcmp(argv[argIndex], "endgame") == 0)
234✔
2937
        {
2938
            if (check_input_amount(argc, argv, argIndex, 1))
×
2939
                exit(1);
×
2940
            use_fused_card_level = atoi(argv[argIndex + 1]);
×
2941
            argIndex += 1;
×
2942
        }
2943
#ifndef NQUEST
2944
        else if (strcmp(argv[argIndex], "quest") == 0)
2945
        {
2946
            if (check_input_amount(argc, argv, argIndex, 1))
2947
                exit(1);
2948
            opt_quest = argv[argIndex + 1];
2949
            argIndex += 1;
2950
        }
2951
#endif
2952
        else if (strcmp(argv[argIndex], "threads") == 0 || strcmp(argv[argIndex], "-t") == 0)
234✔
2953
        {
2954
            if (check_input_amount(argc, argv, argIndex, 1))
78✔
2955
                exit(1);
×
2956
            opt_num_threads = atoi(argv[argIndex + 1]);
78✔
2957
#ifdef _OPENMP
2958
            omp_set_num_threads(opt_num_threads);
2959
#endif
2960
            argIndex += 1;
78✔
2961
        }
2962
        else if (strcmp(argv[argIndex], "target") == 0)
156✔
2963
        {
2964
            if (check_input_amount(argc, argv, argIndex, 1))
×
2965
                exit(1);
×
2966
            opt_target_score = argv[argIndex + 1];
×
2967
            argIndex += 1;
×
2968
        }
2969
        else if (strcmp(argv[argIndex], "turnlimit") == 0)
156✔
2970
        {
2971
            if (check_input_amount(argc, argv, argIndex, 1))
×
2972
                exit(1);
×
2973
            turn_limit = atoi(argv[argIndex + 1]);
×
2974
            argIndex += 1;
×
2975
        }
2976
        else if (strcmp(argv[argIndex], "mis") == 0)
156✔
2977
        {
2978
            if (check_input_amount(argc, argv, argIndex, 1))
×
2979
                exit(1);
×
2980
            min_increment_of_score = atof(argv[argIndex + 1]);
×
2981
            argIndex += 1;
×
2982
        }
2983
        else if (strcmp(argv[argIndex], "timeout") == 0) // set timeout in hours. tuo will stop approx. at the given time.
156✔
2984
        {
2985
            if (check_input_amount(argc, argv, argIndex, 1))
×
2986
                exit(1);
×
2987
            maximum_time = atof(argv[argIndex + 1]);
×
2988
            argIndex += 1;
×
2989
        }
2990
        else if (strcmp(argv[argIndex], "strict-ownedcards") == 0) // won't add missing cards, instead skips faulty decks
156✔
2991
        {
2992
            opt_skip_unclaimed_decks = true;
×
2993
        }
2994
        /*else if (strcmp(argv[argIndex], "trim-ownedcards") == 0) //won't add missing cards, instead removes missing cards from decks
2995
          {
2996
          opt_trim_unclaimed_decks = true;
2997
          }*/
2998
        else if (strcmp(argv[argIndex], "cl") == 0)
156✔
2999
        {
3000
            if (check_input_amount(argc, argv, argIndex, 1))
×
3001
                exit(1);
×
3002
            confidence_level = atof(argv[argIndex + 1]);
×
3003
            argIndex += 1;
×
3004
        }
3005
        else if (strcmp(argv[argIndex], "+so") == 0)
156✔
3006
        {
3007
            simplify_output = true;
×
3008
        }
3009
        else if (strcmp(argv[argIndex], "+uc") == 0)
156✔
3010
        {
3011
            print_upgraded = true;
×
3012
        }
3013
        else if (strcmp(argv[argIndex], "+vc") == 0)
156✔
3014
        {
3015
            print_values = true;
×
3016
        }
3017
        else if (strcmp(argv[argIndex], "+vc-x") == 0)
156✔
3018
        {
3019
            if (check_input_amount(argc, argv, argIndex, 1))
×
3020
                exit(1);
×
3021
            vc_x = atoi(argv[argIndex + 1]);
×
3022
            // print_values = true;
3023
            argIndex += 1;
×
3024
        }
3025
        else if (strcmp(argv[argIndex], "+ci") == 0)
156✔
3026
        {
3027
            show_ci = true;
×
3028
        }
3029
        else if (strcmp(argv[argIndex], "+hm") == 0)
156✔
3030
        {
3031
            use_harmonic_mean = true;
×
3032
        }
3033
        else if (strcmp(argv[argIndex], "no-fix") == 0)
156✔
3034
        {
3035
            for (int i = 0; i < Fix::num_fixes; ++i)
×
3036
                fixes[i] = false;
×
3037
        }
3038
        else if (strcmp(argv[argIndex], "fix-enhance-early") == 0)
156✔
3039
        {
3040
            fixes[Fix::enhance_early] = true;
×
3041
        }
3042
        else if (strcmp(argv[argIndex], "no-fix-enhance-early") == 0)
156✔
3043
        {
3044
            fixes[Fix::enhance_early] = false;
×
3045
        }
3046
        else if (strcmp(argv[argIndex], "fix-revenge-on-death") == 0)
156✔
3047
        {
3048
            fixes[Fix::revenge_on_death] = true;
×
3049
        }
3050
        else if (strcmp(argv[argIndex], "no-fix-revenge-on-death") == 0)
156✔
3051
        {
3052
            fixes[Fix::revenge_on_death] = false;
×
3053
        }
3054
        else if (strcmp(argv[argIndex], "fix-death-from-bge") == 0)
156✔
3055
        {
3056
            fixes[Fix::death_from_bge] = true;
×
3057
        }
3058
        else if (strcmp(argv[argIndex], "no-fix-death-from-bge") == 0)
156✔
3059
        {
3060
            fixes[Fix::death_from_bge] = false;
×
3061
        }
3062
        else if (strcmp(argv[argIndex], "fix-legion-under-megamorphosis") == 0)
156✔
3063
        {
3064
            fixes[Fix::legion_under_mega] = true;
×
3065
        }
3066
        else if (strcmp(argv[argIndex], "no-fix-legion-under-megamorphosis") == 0)
156✔
3067
        {
3068
            fixes[Fix::legion_under_mega] = false;
×
3069
        }
3070
        else if (strcmp(argv[argIndex], "update-barrier-each-turn") == 0)
156✔
3071
                {
3072
                        fixes[Fix::barrier_each_turn] = true;
×
3073
                }
3074
        else if (strcmp(argv[argIndex], "no-update-barrier-each-turn") == 0)
156✔
3075
                {
3076
                        fixes[Fix::barrier_each_turn] = false;
×
3077
                }
3078
        else if (strcmp(argv[argIndex], "update-dont-evade-mimic-selection") == 0)
156✔
3079
                {
3080
                        fixes[Fix::dont_evade_mimic_selection] = true;
×
3081
                }
3082
        else if (strcmp(argv[argIndex], "no-update-dont-evade-mimic-selection") == 0)
156✔
3083
                {
3084
                        fixes[Fix::dont_evade_mimic_selection] = false;
×
3085
                }
3086
        else if (strcmp(argv[argIndex], "update-leech-increase-max-hp") == 0)
156✔
3087
                {
3088
                        fixes[Fix::leech_increase_max_hp] = true;
×
3089
                }
3090
        else if (strcmp(argv[argIndex], "no-update-leech-increase-max-hp") == 0)
156✔
3091
                {
3092
                        fixes[Fix::leech_increase_max_hp] = false;
×
3093
                }
3094
        else if (strcmp(argv[argIndex], "update-counter-without-damage") == 0)
156✔
3095
                {
3096
                        fixes[Fix::counter_without_damage] = true;
×
3097
                }
3098
        else if (strcmp(argv[argIndex], "no-update-counter-without-damage") == 0)
156✔
3099
                {
3100
                        fixes[Fix::counter_without_damage] = false;
×
3101
                }
3102
        else if (strcmp(argv[argIndex], "update-corrosive-protect-armor") == 0)
156✔
3103
                {
3104
                        fixes[Fix::corrosive_protect_armor] = true;
×
3105
                }
3106
        else if (strcmp(argv[argIndex], "no-update-corrosive-protect-armor") == 0)
156✔
3107
                {
3108
                        fixes[Fix::corrosive_protect_armor] = false;
×
3109
                }
3110
        else if (strcmp(argv[argIndex], "update-poison-after-attacked") == 0)
156✔
3111
                {
3112
                        fixes[Fix::poison_after_attacked] = true;
×
3113
                }
3114
        else if (strcmp(argv[argIndex], "no-update-poison-after-attacked") == 0)
156✔
3115
                {
3116
                        fixes[Fix::poison_after_attacked] = false;
×
3117
                }
3118
        else if (strcmp(argv[argIndex], "update-subdue-before-attack") == 0)
156✔
3119
                {
3120
                        fixes[Fix::subdue_before_attack] = true;
×
3121
                }
3122
        else if (strcmp(argv[argIndex], "no-update-subdue-before-attack") == 0)
156✔
3123
                {
3124
                        fixes[Fix::subdue_before_attack] = false;
×
3125
                }
3126
        else if (strcmp(argv[argIndex], "seed") == 0)
156✔
3127
        {
3128
            if (check_input_amount(argc, argv, argIndex, 1))
78✔
3129
                exit(1);
×
3130
            sim_seed = atoi(argv[argIndex + 1]);
78✔
3131
            argIndex += 1;
78✔
3132
        }
3133
        else if (strcmp(argv[argIndex], "-v") == 0)
78✔
3134
        {
3135
            --debug_print;
×
3136
        }
3137
        else if (strcmp(argv[argIndex], "+v") == 0)
78✔
3138
        {
3139
            ++debug_print;
×
3140
        }
3141
        else if (strcmp(argv[argIndex], "vip") == 0)
78✔
3142
        {
3143
            if (check_input_amount(argc, argv, argIndex, 1))
×
3144
                exit(1);
×
3145
            opt_vip = argv[argIndex + 1];
×
3146
            argIndex += 1;
×
3147
        }
3148
        else if (strcmp(argv[argIndex], "allow-candidates") == 0)
78✔
3149
        {
3150
            if (check_input_amount(argc, argv, argIndex, 1))
×
3151
                exit(1);
×
3152
            opt_allow_candidates = argv[argIndex + 1];
×
3153
            argIndex += 1;
×
3154
        }
3155
        else if (strcmp(argv[argIndex], "disallow-candidates") == 0)
78✔
3156
        {
3157
            if (check_input_amount(argc, argv, argIndex, 1))
×
3158
                exit(1);
×
3159
            opt_disallow_candidates = argv[argIndex + 1];
×
3160
            argIndex += 1;
×
3161
        }
3162
        else if (strcmp(argv[argIndex], "disallow-recipes") == 0)
78✔
3163
        {
3164
            if (check_input_amount(argc, argv, argIndex, 1))
×
3165
                exit(1);
×
3166
            opt_disallow_recipes = argv[argIndex + 1];
×
3167
            argIndex += 1;
×
3168
        }
3169
        else if (strcmp(argv[argIndex], "hand") == 0) // set initial hand for test
78✔
3170
        {
3171
            if (check_input_amount(argc, argv, argIndex, 1))
×
3172
                exit(1);
×
3173
            opt_hand = argv[argIndex + 1];
×
3174
            argIndex += 1;
×
3175
        }
3176
        else if (strcmp(argv[argIndex], "enemy:hand") == 0) // set enemies' initial hand for test
78✔
3177
        {
3178
            if (check_input_amount(argc, argv, argIndex, 1))
×
3179
                exit(1);
×
3180
            opt_enemy_hand = argv[argIndex + 1];
×
3181
            argIndex += 1;
×
3182
        }
3183
        else if (strcmp(argv[argIndex], "yf") == 0 || strcmp(argv[argIndex], "yfort") == 0) // set forts
78✔
3184
        {
3185
            if (check_input_amount(argc, argv, argIndex, 1))
×
3186
                exit(1);
×
3187
            opt_forts = std::string(argv[argIndex + 1]);
×
3188
            argIndex += 1;
×
3189
        }
3190
        else if (strcmp(argv[argIndex], "yfpool") == 0 || strcmp(argv[argIndex], "yfortpool") == 0) // set forts
78✔
3191
        {
3192
            if (check_input_amount(argc, argv, argIndex, 1))
×
3193
                exit(1);
×
3194
            yfpool = std::stoi(argv[argIndex + 1]);
×
3195
            argIndex += 1;
×
3196
        }
3197
        else if (strcmp(argv[argIndex], "ef") == 0 || strcmp(argv[argIndex], "efort") == 0) // set enemies' forts
78✔
3198
        {
3199
            if (check_input_amount(argc, argv, argIndex, 1))
×
3200
                exit(1);
×
3201
            opt_enemy_forts = std::string(argv[argIndex + 1]);
×
3202
            argIndex += 1;
×
3203
        }
3204
        else if (strcmp(argv[argIndex], "efpool") == 0 || strcmp(argv[argIndex], "efortpool") == 0) // set forts
78✔
3205
        {
3206
            if (check_input_amount(argc, argv, argIndex, 1))
×
3207
                exit(1);
×
3208
            efpool = std::stoi(argv[argIndex + 1]);
×
3209
            argIndex += 1;
×
3210
        }
3211
        else if (strcmp(argv[argIndex], "yd") == 0 || strcmp(argv[argIndex], "ydom") == 0) // set dominions
78✔
3212
        {
3213
            if (check_input_amount(argc, argv, argIndex, 1))
×
3214
                exit(1);
×
3215
            opt_doms = std::string(argv[argIndex + 1]);
×
3216
            argIndex += 1;
×
3217
        }
3218
        else if (strcmp(argv[argIndex], "ed") == 0 || strcmp(argv[argIndex], "edom") == 0) // set enemies' dominions
78✔
3219
        {
3220
            if (check_input_amount(argc, argv, argIndex, 1))
×
3221
                exit(1);
×
3222
            opt_enemy_doms = std::string(argv[argIndex + 1]);
×
3223
            argIndex += 1;
×
3224
        }
3225
        else if (strcmp(argv[argIndex], "sim") == 0)
78✔
3226
        {
3227
            if (check_input_amount(argc, argv, argIndex, 1))
73✔
3228
                exit(1);
×
3229
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate));
73✔
3230
            if (std::get<0>(opt_todo.back()) < 10)
73✔
3231
            {
3232
                opt_num_threads = 1;
×
3233
            }
3234
            argIndex += 1;
73✔
3235
        }
3236
        // climbing tasks
3237
        else if (strcmp(argv[argIndex], "climbex") == 0)
5✔
3238
        {
3239
            if (check_input_amount(argc, argv, argIndex, 2))
1✔
3240
                exit(1);
×
3241
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), climb));
1✔
3242
            if (std::get<1>(opt_todo.back()) < 10)
1✔
3243
            {
3244
                opt_num_threads = 1;
×
3245
            }
3246
            opt_do_optimization = true;
1✔
3247
            opt_multi_optimization = true;
1✔
3248
            argIndex += 2;
1✔
3249
        }
3250
        else if (strcmp(argv[argIndex], "climb") == 0)
4✔
3251
        {
3252
            if (check_input_amount(argc, argv, argIndex, 1))
1✔
3253
                exit(1);
×
3254
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), climb));
1✔
3255
            if (std::get<1>(opt_todo.back()) < 10)
1✔
3256
            {
3257
                opt_num_threads = 1;
×
3258
            }
3259
            opt_do_optimization = true;
1✔
3260
            opt_multi_optimization = true;
1✔
3261
            argIndex += 1;
1✔
3262
        }
3263
        else if (strcmp(argv[argIndex], "climb_forts") == 0)
3✔
3264
        {
3265
            if (check_input_amount(argc, argv, argIndex, 1))
×
3266
                exit(1);
×
3267
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), climb_forts));
×
3268
            if (std::get<1>(opt_todo.back()) < 10)
×
3269
            {
3270
                opt_num_threads = 1;
×
3271
            }
3272
            argIndex += 1;
×
3273
        }
3274
        else if (strcmp(argv[argIndex], "anneal") == 0)
3✔
3275
        {
3276
            if (check_input_amount(argc, argv, argIndex, 3))
1✔
3277
                exit(1);
×
3278
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), anneal));
1✔
3279
            temperature = std::stod(argv[argIndex + 2]);
2✔
3280
            coolingRate = std::stod(argv[argIndex + 3]);
2✔
3281
            if (std::get<1>(opt_todo.back()) < 10)
1✔
3282
            {
3283
                opt_num_threads = 1;
×
3284
            }
3285
            opt_do_optimization = true;
1✔
3286
            opt_multi_optimization = true;
1✔
3287
            argIndex += 3;
1✔
3288
        }
3289
        else {
3290
            tokenParsed = false;
3291
        }
3292
        
3293
        if (!tokenParsed)
392✔
3294
        {
3295
        //no indent: keep two parts of a nested if at the same level
3296
        if (strcmp(argv[argIndex], "genetic") == 0)
2✔
3297
        {
3298
            if (check_input_amount(argc, argv, argIndex, 1))
1✔
3299
                exit(1);
×
3300
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), genetic));
1✔
3301
            if (std::get<1>(opt_todo.back()) < 10)
1✔
3302
            {
3303
                opt_num_threads = 1;
×
3304
            }
3305
            opt_do_optimization = true;
1✔
3306
            opt_multi_optimization = true;
1✔
3307
            argIndex += 1;
1✔
3308
        }
3309
        else if (strcmp(argv[argIndex], "genetic-pool") == 0 || strcmp(argv[argIndex], "beam-size") == 0 || strcmp(argv[argIndex], "size") == 0)
1✔
3310
        {
3311
            if (check_input_amount(argc, argv, argIndex, 1))
×
3312
                exit(1);
×
3313
            pool_size = std::stod(argv[argIndex + 1]);
×
3314
            argIndex += 1;
×
3315
        }
3316
        else if (strcmp(argv[argIndex], "genetic-gen") == 0)
1✔
3317
        {
3318
            if (check_input_amount(argc, argv, argIndex, 1))
×
3319
                exit(1);
×
3320
            generations = std::stod(argv[argIndex + 1]);
×
3321
            argIndex += 1;
×
3322
        }
3323
        else if (strcmp(argv[argIndex], "genetic-opts") == 0)
1✔
3324
        {
3325
            if (check_input_amount(argc, argv, argIndex, 3))
×
3326
                exit(1);
×
3327
            opt_pool_keep = std::stod(argv[argIndex + 1]);
×
3328
            opt_pool_cross = std::stod(argv[argIndex + 2]);
×
3329
            opt_pool_mutate = std::stod(argv[argIndex + 3]);
×
3330
            argIndex += 3;
×
3331
        }
3332
        else if (strcmp(argv[argIndex], "beam") == 0)
1✔
3333
        {
3334
            if (check_input_amount(argc, argv, argIndex, 1))
1✔
3335
                exit(1);
×
3336
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), beam));
1✔
3337
            if (std::get<1>(opt_todo.back()) < 10)
1✔
3338
            {
3339
                opt_num_threads = 1;
×
3340
            }
3341
            opt_do_optimization = true;
1✔
3342
            opt_multi_optimization = true;
1✔
3343
            argIndex += 1;
1✔
3344
        }
3345
        else if (strcmp(argv[argIndex], "reorder") == 0)
×
3346
        {
3347
            if (check_input_amount(argc, argv, argIndex, 1))
×
3348
                exit(1);
×
3349
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), reorder));
×
3350
            if (std::get<1>(opt_todo.back()) < 10)
×
3351
            {
3352
                opt_num_threads = 1;
×
3353
            }
3354
            opt_do_reorder = true;
×
3355
            argIndex += 1;
×
3356
        }
3357
        else if (strncmp(argv[argIndex], "scale-opts:", 11) == 0)
×
3358
        {
3359
            std::string climb_opts_str(argv[argIndex] + 11);
×
3360
            boost::tokenizer<boost::char_delimiters_separator<char>> climb_opts{climb_opts_str, boost::char_delimiters_separator<char>{false, ",", ""}};
×
3361
            for (const auto &opt : climb_opts)
×
3362
            {
3363
                const auto dot_pos = opt.find(".");
×
3364
                const auto slash_pos = opt.find("/");
×
3365
                const bool has_value = (dot_pos != std::string::npos);
×
3366
                if (slash_pos == std::string::npos)
×
3367
                    throw std::runtime_error("scale-opts:" + opt + " requires an argument");
×
3368
                const std::string &opt_type = has_value ? opt.substr(0, dot_pos) : "";
×
3369
                const std::string opt_name{has_value ? opt.substr(dot_pos + 1, slash_pos - dot_pos - 1) : opt.substr(0, slash_pos)};
×
3370
                const std::string opt_value{opt.substr(slash_pos + 1)};
×
3371
                if ((opt_name == "hp"))
×
3372
                {
3373
                    hp_scale = atof(opt_value.c_str());
×
3374
                }
3375
                else if ((opt_name == "atk"))
×
3376
                {
3377
                    atk_scale = atof(opt_value.c_str());
×
3378
                }
3379
                else if (opt_name == "x")
×
3380
                {
3381
                    x_skill_scale[skill_name_to_id(opt_type)] = atof(opt_value.c_str());
×
3382
                }
3383
                else if (opt_name == "n")
×
3384
                {
3385
                    n_skill_scale[skill_name_to_id(opt_type)] = atof(opt_value.c_str());
×
3386
                }
3387
                else if (opt_name == "c")
×
3388
                {
3389
                    c_skill_scale[skill_name_to_id(opt_type)] = atof(opt_value.c_str());
×
3390
                }
3391
                else
3392
                {
3393
                    std::cerr << "Error: Unknown scale option " << opt_name << " of " << opt_type;
×
3394
                    if (has_value)
×
3395
                    {
3396
                        std::cerr << " (value is: " << opt_value << ")";
×
3397
                    }
3398
                    std::cerr << std::endl;
×
3399
                    exit(1);
×
3400
                }
3401
            }
×
3402
        }
×
3403
        // climbing options
3404
        else if (strncmp(argv[argIndex], "climb-opts:", 11) == 0)
×
3405
        {
3406
            std::string climb_opts_str(argv[argIndex] + 11);
×
3407
            boost::tokenizer<boost::char_delimiters_separator<char>> climb_opts{climb_opts_str, boost::char_delimiters_separator<char>{false, ",", ""}};
×
3408
            for (const auto &opt : climb_opts)
×
3409
            {
3410
                const auto delim_pos = opt.find("=");
×
3411
                const bool has_value = (delim_pos != std::string::npos);
×
3412
                const std::string &opt_name = has_value ? opt.substr(0, delim_pos) : opt;
×
3413
                const std::string opt_value{has_value ? opt.substr(delim_pos + 1) : opt};
×
3414
                auto ensure_opt_value = [](const bool has_value, const std::string &opt_name)
×
3415
                {
3416
                    if (!has_value)
×
3417
                    {
3418
                        throw std::runtime_error("climb-opts:" + opt_name + " requires an argument");
×
3419
                    }
3420
                };
×
3421
                if ((opt_name == "iter-mul") or (opt_name == "iterations-multiplier"))
×
3422
                {
3423
                    ensure_opt_value(has_value, opt_name);
×
3424
                    iterations_multiplier = std::stoi(opt_value);
×
3425
                }
3426
                else if ((opt_name == "egc") or (opt_name == "endgame-commander") or (opt_name == "min-commander-fusion-level"))
×
3427
                {
3428
                    ensure_opt_value(has_value, opt_name);
×
3429
                    use_fused_commander_level = std::stoi(opt_value);
×
3430
                }
3431
                else if (opt_name == "use-all-commander-levels")
×
3432
                {
3433
                    use_top_level_commander = false;
×
3434
                }
3435
                else if (opt_name == "use-all-card-levels")
×
3436
                {
3437
                    use_top_level_card = false;
×
3438
                }
3439
                else if ((opt_name == "recent-boost") or (opt_name == "rb")) // prefer new cards in hill climb and break climb loop faster
×
3440
                {
3441
                    prefered_recent = true;
×
3442
                }
3443
                else if ((opt_name == "recent-boost-times") or (opt_name == "rbt")) // prefer new cards in hill climb and break climb loop faster
×
3444
                {
3445
                    ensure_opt_value(has_value, opt_name);
×
3446
                    prefered_factor = std::stoi(opt_value);
×
3447
                }
3448
                else if ((opt_name == "recent-boost-percent") or (opt_name == "rbp")) // prefer new cards in hill climb and break climb loop faster
×
3449
                {
3450
                    ensure_opt_value(has_value, opt_name);
×
3451
                    recent_percent = std::stoi(opt_value);
×
3452
                }
3453
                else if ((opt_name == "otd") or (opt_name == "open-the-deck"))
×
3454
                {
3455
                    mode_open_the_deck = true;
×
3456
                }
3457
                else
3458
                {
3459
                    std::cerr << "Error: Unknown climb option " << opt_name;
×
3460
                    if (has_value)
×
3461
                    {
3462
                        std::cerr << " (value is: " << opt_value << ")";
×
3463
                    }
3464
                    std::cerr << std::endl;
×
3465
                    exit(1);
×
3466
                }
3467
            }
×
3468
        }
×
3469
        else if (strcmp(argv[argIndex], "debug") == 0)
×
3470
        {
3471
            opt_todo.push_back(std::make_tuple(0u, 0u, debug));
×
3472
            opt_num_threads = 1;
×
3473
            // disable saving to db
3474
            use_db_write= false;
×
3475
            use_db_load = false;
×
3476
        }
3477
        else if (strcmp(argv[argIndex], "debuguntil") == 0)
×
3478
        {
3479
            // output the debug info for the first battle that min_score <= score <= max_score.
3480
            // E.g., 0 0: lose; 100 100: win (non-raid); 20 100: at least 20 damage (raid).
3481
            if (check_input_amount(argc, argv, argIndex, 2))
×
3482
                exit(1);
×
3483
            opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil));
×
3484
            opt_num_threads = 1;
×
3485
            argIndex += 2;
×
3486
            // disable saving to db
3487
            use_db_write= false;
×
3488
            use_db_load = false;
×
3489
        }
3490
        else
3491
        {
3492
            std::cerr << "Error: Unknown option " << argv[argIndex] << std::endl;
×
3493
            exit(1);
×
3494
        }
3495
        } // if (tokenParsed)
3496
    }
3497
    load_db(prefix);
156✔
3498
    load_ml(prefix);
156✔
3499

3500
#ifdef _OPENMP
3501
    opt_num_threads = omp_get_max_threads();
3502
#endif
3503
    // delete, since prefix/suffix might change we reload all cards.
3504
    // redundant to calling init() before run()
3505
    all_cards.clear();
78✔
3506
    owned_alpha_dominion = nullptr;
78✔
3507

3508
    all_cards = Cards();
78✔
3509
    Decks decks;
78✔
3510
    std::unordered_map<std::string, std::string> bge_aliases;
78✔
3511
    load_skills_set_xml(all_cards, prefix + "data/skills_set.xml", true);
78✔
3512
    for (unsigned section = 1;
1,560✔
3513
         load_cards_xml(all_cards, prefix + "data/cards_section_" + std::to_string(section) + ".xml", false);
3,120✔
3514
         ++section)
3515
        ;
3516
    all_cards.organize();
78✔
3517
    load_levels_xml(all_cards, prefix + "data/levels.xml", true);
78✔
3518
    all_cards.fix_dominion_recipes();
78✔
3519
    for (const auto &suffix : fn_suffix_list)
156✔
3520
    {
3521
        load_decks_xml(decks, all_cards, prefix + "data/missions" + suffix + ".xml", prefix + "data/raids" + suffix + ".xml", suffix.empty());
468✔
3522
        load_recipes_xml(all_cards, prefix + "data/fusion_recipes_cj2" + suffix + ".xml", suffix.empty());
312✔
3523
        read_card_abbrs(all_cards, prefix + "data/cardabbrs" + suffix + ".txt");
312✔
3524
    }
3525
    for (const auto &suffix : fn_suffix_list)
156✔
3526
    {
3527
        load_custom_decks(decks, all_cards, prefix + "data/customdecks" + suffix + ".txt");
312✔
3528
        map_keys_to_set(read_custom_cards(all_cards, prefix + "data/allowed_candidates" + suffix + ".txt", false), allowed_candidates);
312✔
3529
        map_keys_to_set(read_custom_cards(all_cards, prefix + "data/disallowed_candidates" + suffix + ".txt", false), disallowed_candidates);
312✔
3530
        map_keys_to_set(read_custom_cards(all_cards, prefix + "data/disallowed_recipes" + suffix + ".txt", false), disallowed_recipes);
312✔
3531
    }
3532

3533
    read_bge_aliases(bge_aliases, prefix + "data/bges.txt");
78✔
3534

3535
    fill_skill_table();
78✔
3536

3537
    if (opt_do_optimization and use_owned_cards)
78✔
3538
    {
3539
        if (opt_owned_cards_str_list.empty())
5✔
3540
        { // load default files only if specify no -o=
3541
            for (const auto &suffix : fn_suffix_list)
10✔
3542
            {
3543
                std::string filename = prefix + "data/ownedcards" + suffix + ".txt";
15✔
3544
                if (boost::filesystem::exists(filename))
15✔
3545
                {
3546
                    opt_owned_cards_str_list.push_back(filename);
5✔
3547
                }
3548
            }
5✔
3549
        }
3550
        std::map<unsigned, unsigned> _owned_cards;
5✔
3551
        for (const auto &oc_str : opt_owned_cards_str_list)
10✔
3552
        {
3553
            read_owned_cards(all_cards, _owned_cards, oc_str);
5✔
3554
        }
3555

3556
        // keep only one copy of alpha dominion
3557
        for (auto owned_it = _owned_cards.begin(); owned_it != _owned_cards.end();)
125✔
3558
        {
3559
            const Card *owned_card = all_cards.by_id(owned_it->first);
120✔
3560
            bool need_remove = (!owned_it->second);
120✔
3561
            if (!need_remove && (owned_card->m_category == CardCategory::dominion_alpha))
120✔
3562
            {
3563
                if (!owned_alpha_dominion)
15✔
3564
                {
3565
                    owned_alpha_dominion = owned_card;
5✔
3566
                }
3567
                else
3568
                {
3569
                    /*std::cerr << "Warning: ownedcards already contains alpha dominion (" << owned_alpha_dominion->m_name
3570
                      << "): removing additional " << owned_card->m_name << std::endl;
3571
                      need_remove = true;*/
3572
                }
3573
            }
3574
            if (need_remove)
120✔
3575
            {
3576
                owned_it = _owned_cards.erase(owned_it);
×
3577
            }
3578
            else
3579
            {
3580
                ++owned_it;
120✔
3581
            }
3582
        }
3583
        if (!owned_alpha_dominion && use_owned_dominions)
5✔
3584
        {
3585
            // owned_alpha_dominion = all_cards.by_id(50002);
3586
            // std::cerr << "Warning: dominion climbing enabled and no alpha dominion found in owned cards, adding default "
3587
            //     << owned_alpha_dominion->m_name << std::endl;
3588
        }
3589
        if (owned_alpha_dominion)
5✔
3590
        {
3591
            _owned_cards[owned_alpha_dominion->m_id] = 1;
5✔
3592
        }
3593

3594
        // remap owned cards to unordered map (should be quicker for searching)
3595
        owned_cards.reserve(_owned_cards.size());
5✔
3596
        for (auto owned_it = _owned_cards.begin(); owned_it != _owned_cards.end(); ++owned_it)
125✔
3597
        {
3598
            owned_cards[owned_it->first] = owned_it->second;
120✔
3599
        }
3600
    }
5✔
3601

3602
    // parse BGEs
3603
    opt_bg_effects[0].fill(0);
2,184✔
3604
    opt_bg_effects[1].fill(0);
312✔
3605
    for (int player = 2; player >= 0; --player)
312✔
3606
    {
3607
        for (auto &&opt_effect : opt_effects[player])
293✔
3608
        {
3609
            std::unordered_set<std::string> used_bge_aliases;
59✔
3610
            if (!parse_bge(opt_effect, player, bge_aliases, opt_bg_effects[0], opt_bg_effects[1], opt_bg_skills[0], opt_bg_skills[1], used_bge_aliases))
177✔
3611
            {
3612
                exit(1);
×
3613
            }
3614
        }
59✔
3615
    }
3616

3617
    // parse allowed candidates from options
3618
    try
78✔
3619
    {
3620
        auto &&id_marks = string_to_ids(all_cards, opt_allow_candidates, "allowed-candidates");
78✔
3621
        for (const auto &cid : id_marks.first)
78✔
3622
        {
3623
            allowed_candidates.insert(cid);
×
3624
        }
3625
    }
×
3626
    catch (const std::runtime_error &e)
×
3627
    {
3628
        std::cerr << "Error: allow-candidates " << opt_allow_candidates << ": " << e.what() << std::endl;
×
3629
        exit(1);
×
3630
    }
×
3631

3632
    // parse disallowed candidates from options
3633
    try
78✔
3634
    {
3635
        auto &&id_marks = string_to_ids(all_cards, opt_disallow_candidates, "disallowed-candidates");
78✔
3636
        for (const auto &cid : id_marks.first)
78✔
3637
        {
3638
            disallowed_candidates.insert(cid);
×
3639
        }
3640
    }
×
3641
    catch (const std::runtime_error &e)
×
3642
    {
3643
        std::cerr << "Error: disallow-candidates " << opt_disallow_candidates << ": " << e.what() << std::endl;
×
3644
        exit(1);
×
3645
    }
×
3646

3647
    // parse & drop disallowed recipes
3648
    try
78✔
3649
    {
3650
        auto &&id_dis_recipes = string_to_ids(all_cards, opt_disallow_recipes, "disallowed-recipes");
78✔
3651
        for (auto &cid : id_dis_recipes.first)
78✔
3652
        {
3653
            all_cards.erase_fusion_recipe(cid);
×
3654
        }
3655
    }
×
3656
    catch (const std::runtime_error &e)
×
3657
    {
3658
        std::cerr << "Error: disallow-recipes " << opt_disallow_recipes << ": " << e.what() << std::endl;
×
3659
        exit(1);
×
3660
    }
×
3661
    for (auto cid : disallowed_recipes)
78✔
3662
    {
3663
        all_cards.erase_fusion_recipe(cid);
×
3664
    }
3665

3666
#ifndef NQUEST
3667
    if (!opt_quest.empty())
3668
    {
3669
        try
3670
        {
3671
            optimization_mode = OptimizationMode::quest;
3672
            std::vector<std::string> tokens;
3673
            boost::split(tokens, opt_quest, boost::is_any_of(" -"));
3674
            if (tokens.size() < 3)
3675
            {
3676
                throw std::runtime_error("Expect one of: su n skill; sd n skill; cu n faction/strcture; ck n structure");
3677
            }
3678
            auto type_str = boost::to_lower_copy(tokens[0]);
3679
            tuo::quest.quest_value = boost::lexical_cast<unsigned>(tokens[1]);
3680
            auto key_str = boost::to_lower_copy(tokens[2]);
3681
            unsigned quest_index = 3;
3682
            if (type_str == "su" || type_str == "sd")
3683
            {
3684
                Skill::Skill skill_id = skill_name_to_id(key_str);
3685
                if (skill_id == Skill::no_skill)
3686
                {
3687
                    std::cerr << "Error: Expect skill in quest \"" << opt_quest << "\".\n";
3688
                    exit(1);
3689
                }
3690
                tuo::quest.quest_type = type_str == "su" ? QuestType::skill_use : QuestType::skill_damage;
3691
                tuo::quest.quest_key = skill_id;
3692
            }
3693
            else if (type_str == "cu" || type_str == "ck")
3694
            {
3695
                if (key_str == "assault")
3696
                {
3697
                    tuo::quest.quest_type = type_str == "cu" ? QuestType::type_card_use : QuestType::type_card_kill;
3698
                    tuo::quest.quest_key = CardType::assault;
3699
                }
3700
                else if (key_str == "structure")
3701
                {
3702
                    tuo::quest.quest_type = type_str == "cu" ? QuestType::type_card_use : QuestType::type_card_kill;
3703
                    tuo::quest.quest_key = CardType::structure;
3704
                }
3705
                else
3706
                {
3707
                    for (unsigned i = 1; i < Faction::num_factions; ++i)
3708
                    {
3709
                        if (key_str == boost::to_lower_copy(faction_names[i]))
3710
                        {
3711
                            tuo::quest.quest_type = type_str == "cu" ? QuestType::faction_assault_card_use : QuestType::faction_assault_card_kill;
3712
                            tuo::quest.quest_key = i;
3713
                            break;
3714
                        }
3715
                    }
3716
                    if (tuo::quest.quest_key == 0)
3717
                    {
3718
                        std::cerr << "Error: Expect assault, structure or faction in quest \"" << opt_quest << "\".\n";
3719
                        exit(1);
3720
                    }
3721
                }
3722
            }
3723
            else if (type_str == "cs")
3724
            {
3725
                unsigned card_id;
3726
                unsigned card_num;
3727
                char num_sign;
3728
                char mark;
3729
                try
3730
                {
3731
                    parse_card_spec(all_cards, key_str, card_id, card_num, num_sign, mark);
3732
                    tuo::quest.quest_type = QuestType::card_survival;
3733
                    tuo::quest.quest_key = card_id;
3734
                }
3735
                catch (const std::runtime_error &e)
3736
                {
3737
                    std::cerr << "Error: Expect a card in quest \"" << opt_quest << "\".\n";
3738
                    exit(1);
3739
                }
3740
            }
3741
            else if (type_str == "suoc" && tokens.size() >= 4)
3742
            {
3743
                Skill::Skill skill_id = skill_name_to_id(key_str);
3744
                if (skill_id == Skill::no_skill)
3745
                {
3746
                    std::cerr << "Error: Expect skill in quest \"" << opt_quest << "\".\n";
3747
                    exit(1);
3748
                }
3749
                unsigned card_id;
3750
                unsigned card_num;
3751
                char num_sign;
3752
                char mark;
3753
                try
3754
                {
3755
                    parse_card_spec(all_cards, boost::to_lower_copy(tokens[3]), card_id, card_num, num_sign, mark);
3756
                    quest_index += 1;
3757
                    tuo::quest.quest_type = QuestType::skill_use;
3758
                    tuo::quest.quest_key = skill_id;
3759
                    tuo::quest.quest_2nd_key = card_id;
3760
                }
3761
                catch (const std::runtime_error &e)
3762
                {
3763
                    std::cerr << "Error: Expect a card in quest \"" << opt_quest << "\".\n";
3764
                    exit(1);
3765
                }
3766
            }
3767
            else
3768
            {
3769
                throw std::runtime_error("Expect one of: su n skill; sd n skill; cu n faction/strcture; ck n structure");
3770
            }
3771
            tuo::quest.quest_score = tuo::quest.quest_value;
3772
            for (unsigned i = quest_index; i < tokens.size(); ++i)
3773
            {
3774
                const auto &token = tokens[i];
3775
                if (token == "each")
3776
                {
3777
                    tuo::quest.must_fulfill = true;
3778
                    tuo::quest.quest_score = 100;
3779
                }
3780
                else if (token == "win")
3781
                {
3782
                    tuo::quest.must_win = true;
3783
                }
3784
                else if (token.substr(0, 2) == "q=")
3785
                {
3786
                    tuo::quest.quest_score = boost::lexical_cast<unsigned>(token.substr(2));
3787
                }
3788
                else if (token.substr(0, 2) == "w=")
3789
                {
3790
                    tuo::quest.win_score = boost::lexical_cast<unsigned>(token.substr(2));
3791
                }
3792
                else
3793
                {
3794
                    throw std::runtime_error("Cannot recognize " + token);
3795
                }
3796
            }
3797
            max_possible_score[(size_t)optimization_mode] = tuo::quest.quest_score + tuo::quest.win_score;
3798
        }
3799
        catch (const boost::bad_lexical_cast &e)
3800
        {
3801
            std::cerr << "Error: Expect a number in quest \"" << opt_quest << "\".\n";
3802
            exit(1);
3803
        }
3804
        catch (const std::runtime_error &e)
3805
        {
3806
            std::cerr << "Error: quest " << opt_quest << ": " << e.what() << std::endl;
3807
            exit(1);
3808
        }
3809
    }
3810
#endif
3811

3812
    // std::string your_deck_name{argv[1]};
3813
    auto &&your_deck_list_parsed = parse_deck_list(your_deck_list, decks);
156✔
3814
    auto &&enemy_deck_list_parsed = parse_deck_list(enemy_deck_list, decks);
156✔
3815

3816
    // Deck* your_deck{nullptr};
3817
    std::vector<Deck *> your_decks;
78✔
3818
    std::vector<Deck *> enemy_decks;
78✔
3819
    std::vector<long double> your_decks_factors;
78✔
3820
    std::vector<long double> enemy_decks_factors;
78✔
3821
    for (auto deck_parsed : your_deck_list_parsed)
375✔
3822
    {
3823
        Deck *your_deck{nullptr};
297✔
3824
        try
297✔
3825
        {
3826
            your_deck = find_deck(decks, all_cards, deck_parsed.first)->clone();
594✔
3827
        }
3828
        catch (const std::runtime_error &e)
×
3829
        {
3830
            std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl;
×
3831
            exit(1);
×
3832
        }
×
3833
        if (your_deck == nullptr)
297✔
3834
        {
3835
            std::cerr << "Error: Invalid attack deck name/hash " << deck_parsed.first << ".\n";
×
3836
            exit(1);
×
3837
        }
3838
        else if (!your_deck->variable_cards.empty())
297✔
3839
        {
3840
            std::cerr << "Error: Invalid attack deck " << deck_parsed.first << ": has optional cards.\n";
×
3841
            your_deck = nullptr;
×
3842
        }
3843
        else if (!your_deck->variable_forts.empty())
297✔
3844
        {
3845
            std::cerr << "Error: Invalid attack deck " << deck_parsed.first << ": has optional cards.\n";
×
3846
            your_deck = nullptr;
×
3847
        }
3848
        if (your_deck == nullptr)
297✔
3849
        {
3850
            usage(argc, argv);
×
3851
            exit(1);
×
3852
            // return 255;
3853
        }
3854

3855
        your_deck->strategy = opt_your_strategy;
297✔
3856
        if (!opt_forts.empty())
297✔
3857
        {
3858
            try
×
3859
            {
3860
                if (!yfpool)
×
3861
                    your_deck->add_forts(opt_forts + ",");
×
3862
                else
3863
                    your_deck->add_pool_forts(opt_forts + ",", yfpool);
×
3864
            }
3865
            catch (const std::runtime_error &e)
×
3866
            {
3867
                std::cerr << "Error: yfort " << opt_forts << ": " << e.what() << std::endl;
×
3868
                exit(1);
×
3869
            }
×
3870
        }
3871
        if (!opt_doms.empty())
297✔
3872
        {
3873
            try
×
3874
            {
3875
                your_deck->add_dominions(opt_doms + ",", true);
×
3876
            }
3877
            catch (const std::runtime_error &e)
×
3878
            {
3879
                std::cerr << "Error: ydom " << opt_doms << ": " << e.what() << std::endl;
×
3880
                exit(1);
×
3881
            }
×
3882
        }
3883

3884
        try
297✔
3885
        {
3886
            your_deck->set_vip_cards(opt_vip);
297✔
3887
        }
3888
        catch (const std::runtime_error &e)
×
3889
        {
3890
            std::cerr << "Error: vip " << opt_vip << ": " << e.what() << std::endl;
×
3891
            exit(1);
×
3892
        }
×
3893

3894
        try
297✔
3895
        {
3896
            your_deck->set_given_hand(opt_hand);
297✔
3897
        }
3898
        catch (const std::runtime_error &e)
×
3899
        {
3900
            std::cerr << "Error: hand " << opt_hand << ": " << e.what() << std::endl;
×
3901
            exit(1);
×
3902
        }
×
3903

3904
        // add cards from the decks to requirement/inventory
3905
        if (opt_do_optimization || opt_do_reorder)
297✔
3906
        {
3907
            if (opt_keep_commander) // TODO this does not work with multi deck mode
5✔
3908
            {
3909
                requirement.num_cards[your_deck->commander] = 1;
×
3910
            }
3911
            for (auto &&card_mark : your_deck->card_marks)
5✔
3912
            {
3913
                auto &&card = card_mark.first < 0 ? your_deck->commander : your_deck->cards[card_mark.first];
×
3914
                auto mark = card_mark.second;
×
3915
                if ((mark == '!') && ((card_mark.first >= 0) || !opt_keep_commander))
×
3916
                {
3917
                    requirement.num_cards[card] += 1;
×
3918
                }
3919
            }
3920
            if (opt_skip_unclaimed_decks)
5✔
3921
            {
3922
                // skip decks that can not be build
3923
                if (claim_cards_needed({your_deck->commander}))
×
3924
                    continue;
×
3925
                if (claim_cards_needed(your_deck->cards))
×
3926
                    continue;
×
3927
                if (your_deck->alpha_dominion && claim_cards_needed({your_deck->alpha_dominion}))
×
3928
                    continue;
×
3929
            }
3930
            else if (opt_do_optimization and use_owned_cards)
5✔
3931
            {
3932
                // Force to claim cards in your initial deck.
3933
                claim_cards({your_deck->commander});
5✔
3934
                claim_cards(your_deck->cards);
5✔
3935
                if (your_deck->alpha_dominion)
5✔
3936
                    claim_cards({your_deck->alpha_dominion});
×
3937
            }
3938

3939
            // shrink any oversized deck to maximum of 10 cards + commander
3940
            // NOTE: do this AFTER the call to claim_cards so that passing an initial deck of >10 cards
3941
            //       can be used as a "shortcut" for adding them to owned cards. Also this allows climb
3942
            //       to figure out which are the best 10, rather than restricting climb to the first 10.
3943
            if (your_deck->cards.size() > max_deck_len)
5✔
3944
            {
3945
                your_deck->shrink(max_deck_len);
×
3946
                if (debug_print >= 0)
×
3947
                {
3948
                    std::cerr << "WARNING: Too many cards in your deck. Trimmed.\n";
×
3949
                }
3950
            }
3951
        }
3952

3953
        your_decks.push_back(your_deck);
297✔
3954
        your_decks_factors.push_back(deck_parsed.second);
297✔
3955
    }
297✔
3956
    if (your_decks.size() == 0)
78✔
3957
    {
3958
        std::cerr << "No deck set. Probably due to 'strict-ownedcards' and a deck that is incompatible to the set 'ownedcards'." << std::endl;
×
3959
        exit(1);
×
3960
    }
3961
    target_score = opt_target_score.empty() ? max_possible_score[(size_t)optimization_mode] : boost::lexical_cast<long double>(opt_target_score);
78✔
3962

3963
    for (auto deck_parsed : enemy_deck_list_parsed)
375✔
3964
    {
3965
        Deck *enemy_deck{nullptr};
297✔
3966
        try
297✔
3967
        {
3968
            enemy_deck = find_deck(decks, all_cards, deck_parsed.first);
594✔
3969
        }
3970
        catch (const std::runtime_error &e)
×
3971
        {
3972
            std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl;
×
3973
            exit(1);
×
3974
        }
×
3975
        if (enemy_deck == nullptr)
297✔
3976
        {
3977
            std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ".\n";
×
3978
            usage(argc, argv);
×
3979
            exit(1);
×
3980
        }
3981
        if (optimization_mode == OptimizationMode::notset)
297✔
3982
        {
3983
            if (enemy_deck->decktype == DeckType::raid)
78✔
3984
            {
3985
                optimization_mode = OptimizationMode::raid;
×
3986
            }
3987
            else if (enemy_deck->decktype == DeckType::campaign)
78✔
3988
            {
3989
                gamemode = surge;
×
3990
                optimization_mode = OptimizationMode::campaign;
×
3991
            }
3992
            else
3993
            {
3994
                optimization_mode = OptimizationMode::winrate;
78✔
3995
            }
3996
        }
3997
        enemy_deck->strategy = opt_enemy_strategy;
297✔
3998
        if (!opt_enemy_doms.empty())
297✔
3999
        {
4000
            try
×
4001
            {
4002
                enemy_deck->add_dominions(opt_enemy_doms + ",", true);
×
4003
            }
4004
            catch (const std::runtime_error &e)
×
4005
            {
4006
                std::cerr << "Error: edom " << opt_enemy_doms << ": " << e.what() << std::endl;
×
4007
                exit(1);
×
4008
            }
×
4009
        }
4010
        if (!opt_enemy_forts.empty())
297✔
4011
        {
4012
            try
×
4013
            {
4014
                if (!efpool)
×
4015
                    enemy_deck->add_forts(opt_enemy_forts + ",");
×
4016
                else
4017
                    enemy_deck->add_pool_forts(opt_enemy_forts + ",", efpool);
×
4018
            }
4019
            catch (const std::runtime_error &e)
×
4020
            {
4021
                std::cerr << "Error: efort " << opt_enemy_forts << ": " << e.what() << std::endl;
×
4022
                exit(1);
×
4023
            }
×
4024
        }
4025
        try
297✔
4026
        {
4027
            enemy_deck->set_given_hand(opt_enemy_hand);
297✔
4028
        }
4029
        catch (const std::runtime_error &e)
×
4030
        {
4031
            std::cerr << "Error: enemy:hand " << opt_enemy_hand << ": " << e.what() << std::endl;
×
4032
            exit(1);
×
4033
        }
×
4034
        enemy_decks.push_back(enemy_deck);
297✔
4035
        enemy_decks_factors.push_back(deck_parsed.second);
297✔
4036
    }
297✔
4037

4038
    std::vector<long double> factors((opt_multi_optimization ? 1 : your_decks_factors.size()) * enemy_decks_factors.size());
78✔
4039
    for (unsigned i = 0; i < factors.size(); ++i)
2,087✔
4040
    {
4041
        factors[i] = your_decks_factors[i / enemy_decks_factors.size()] * enemy_decks_factors[i % enemy_decks_factors.size()];
2,009✔
4042
    }
4043

4044
    if ((opt_do_optimization || opt_do_reorder) && (your_decks.size() != 1 && !opt_multi_optimization))
78✔
4045
    {
4046
        std::cerr << "Optimization only works with a single deck" << std::endl;
×
4047
        exit(1);
×
4048
    }
4049

4050
    if (debug_print >= 0)
78✔
4051
    {
4052
        for (unsigned i(0); i < your_decks.size(); ++i)
375✔
4053
        {
4054
            auto your_deck = your_decks[i];
297✔
4055
            std::cout << "Your Deck:" << your_decks_factors[i] << ": " << (debug_print > 0 ? your_deck->long_description() : your_deck->medium_description()) << std::endl;
594✔
4056
        }
4057
        for (unsigned bg_effect = PassiveBGE::no_bge; bg_effect < PassiveBGE::num_passive_bges; ++bg_effect)
2,184✔
4058
        {
4059
            auto bge_value = opt_bg_effects[0][bg_effect];
2,106✔
4060
            if (!bge_value)
2,106✔
4061
                continue;
2,072✔
4062
            std::cout << "Your BG Effect: " << passive_bge_names[bg_effect];
34✔
4063
            if (bge_value != -1)
34✔
4064
                std::cout << " " << bge_value;
34✔
4065
            std::cout << std::endl;
2,106✔
4066
        }
4067
        for (const auto &bg_skill : opt_bg_skills[0])
79✔
4068
        {
4069
            std::cout << "Your BG Skill: " << skill_description(all_cards, bg_skill) << std::endl;
1✔
4070
        }
4071

4072
        for (unsigned i(0); i < enemy_decks.size(); ++i)
375✔
4073
        {
4074
            auto enemy_deck = enemy_decks[i];
297✔
4075
            std::cout << "Enemy's Deck:" << enemy_decks_factors[i] << ": "
297✔
4076
                      << (debug_print > 0 ? enemy_deck->long_description() : enemy_deck->medium_description()) << std::endl;
594✔
4077
        }
4078
        for (unsigned bg_effect = PassiveBGE::no_bge; bg_effect < PassiveBGE::num_passive_bges; ++bg_effect)
2,184✔
4079
        {
4080
            auto bge_value = opt_bg_effects[1][bg_effect];
2,106✔
4081
            if (!bge_value)
2,106✔
4082
                continue;
2,072✔
4083
            std::cout << "Enemy's BG Effect: " << passive_bge_names[bg_effect];
34✔
4084
            if (bge_value != -1)
34✔
4085
                std::cout << " " << bge_value;
34✔
4086
            std::cout << std::endl;
2,106✔
4087
        }
4088
        for (const auto &bg_skill : opt_bg_skills[1])
79✔
4089
        {
4090
            std::cout << "Enemy's BG Skill: " << skill_description(all_cards, bg_skill) << std::endl;
1✔
4091
        }
4092
    }
4093
    if (enemy_decks.size() == 1)
78✔
4094
    {
4095
        auto enemy_deck = enemy_decks[0];
43✔
4096
        for (auto x_mult_ss : enemy_deck->effects)
43✔
4097
        {
4098
            if (debug_print >= 0)
×
4099
            {
4100
                std::cout << "Enemy's X-Mult BG Skill (effective X = round_up[X * " << enemy_deck->level << "]): "
×
4101
                          << skill_description(all_cards, x_mult_ss);
×
4102
                if (x_mult_ss.x)
×
4103
                {
4104
                    std::cout << " (eff. X = " << ceil(x_mult_ss.x * enemy_deck->level) << ")";
×
4105
                }
4106
                std::cout << std::endl;
×
4107
            }
4108
            opt_bg_skills[1].push_back({x_mult_ss.id,
×
4109
                                        (unsigned)ceil(x_mult_ss.x * enemy_deck->level),
×
4110
                                        x_mult_ss.y, x_mult_ss.n, x_mult_ss.c,
×
4111
                                        x_mult_ss.s, x_mult_ss.s2, x_mult_ss.all});
×
4112
        }
4113
    }
4114
    auto proc_decks = your_decks;
78✔
4115
    if (opt_multi_optimization)
78✔
4116
    { // only one deck at a time
4117
        proc_decks.erase(proc_decks.begin() + 1, proc_decks.end());
5✔
4118
    }
4119
    Process p(opt_num_threads, all_cards, decks, proc_decks, enemy_decks, factors, gamemode,
78✔
4120
#ifndef NQUEST
4121
              tuo::quest,
4122
#endif
4123
              opt_bg_effects[0], opt_bg_effects[1], opt_bg_skills[0], opt_bg_skills[1]);
78✔
4124

4125
    auto your_deck = your_decks[0];
78✔
4126

4127
    if (!opt_todo.empty())
78✔
4128
    {
4129
    auto op = opt_todo.back();
78✔
4130
    // for (auto op: opt_todo)
4131
    {
78✔
4132
        switch (std::get<2>(op))
78✔
4133
        {
4134
        case noop:
4135
            break;
4136
        case simulate:
73✔
4137
        {
73✔
4138
            EvaluatedResults results = {EvaluatedResults::first_type(enemy_decks.size() * your_decks.size()), 0};
73✔
4139
            results = p.evaluate(std::get<0>(op), results);
73✔
4140
            print_results(results, p.factors);
73✔
4141
            fr = std::make_pair(your_deck->clone(), compute_score(results, p.factors));
73✔
4142
            print_sim_card_values(fr.first, p, std::get<0>(op));
73✔
4143
            break;
73✔
4144
        }
73✔
4145
        case climb_forts:
×
4146
        {
×
4147
            fr = forts_climbing(std::get<0>(op), p);
×
4148
            break;
×
4149
        }
4150
        case climb:
2✔
4151
        {
2✔
4152
            // TODO check for your_decks.size()==1
4153
            fr = hill_climbing(std::get<0>(op), std::get<1>(op), your_decks, p, requirement
2✔
4154
#ifndef NQUEST
4155
                               ,
4156
                               tuo::quest
4157
#endif
4158
            );
2✔
4159
            break;
2✔
4160
        }
4161
        case anneal:
1✔
4162
        {
1✔
4163
            // TODO check for your_decks.size()==1
4164
            fr = simulated_annealing(std::get<0>(op), std::get<1>(op), your_decks, p, requirement
1✔
4165
#ifndef NQUEST
4166
                                     ,
4167
                                     tuo::quest
4168
#endif
4169
            );
1✔
4170
            break;
1✔
4171
        }
4172
        case genetic:
1✔
4173
        {
1✔
4174
            fr = genetic_algorithm(std::get<0>(op), std::get<1>(op), your_decks, p, requirement
1✔
4175
#ifndef NQUEST
4176
                                   ,
4177
                                   tuo::quest
4178
#endif
4179
            );
1✔
4180
            break;
1✔
4181
        }
4182

4183
        case beam:
1✔
4184
        {
1✔
4185
            fr = beam_climb(std::get<0>(op), std::get<1>(op), your_decks, p, requirement
1✔
4186
#ifndef NQUEST
4187
                            ,
4188
                            tuo::quest
4189
#endif
4190
            );
1✔
4191
            break;
1✔
4192
        }
4193
        case reorder:
×
4194
        {
×
4195
            // TODO multi deck mode for reorder
4196
            your_deck->strategy = DeckStrategy::ordered;
×
4197
            use_owned_cards = true;
×
4198
            use_top_level_card = false;
×
4199
            use_top_level_commander = false;
×
4200
            use_owned_dominions = false;
×
4201
            if (min_deck_len == 1 && max_deck_len == 10)
×
4202
            {
4203
                min_deck_len = max_deck_len = your_deck->cards.size();
×
4204
            }
4205
            fund = 0;
×
4206
            debug_print = -1;
×
4207
            owned_cards.clear();
×
4208
            claim_cards({your_deck->commander});
×
4209
            claim_cards(your_deck->cards);
×
4210
            std::vector<Deck *> single_deck = {your_deck};
×
4211
            fr = hill_climbing(std::get<0>(op), std::get<1>(op), single_deck, p, requirement
×
4212
#ifndef NQUEST
4213
                               ,
4214
                               tuo::quest
4215
#endif
4216
            );
×
4217
            break;
×
4218
        }
×
4219
            // fr=nllptr no return
4220
        case debug:
×
4221
        {
×
4222
            ++debug_print;
×
4223
            debug_str.clear();
×
4224
            EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0};
×
4225
            results = p.evaluate(1, results);
×
4226
            print_results(results, p.factors);
×
4227
            --debug_print;
×
4228
            break;
×
4229
        }
×
4230
            // fr=nllptr no return
4231
        case debuguntil:
×
4232
        {
×
4233
            ++debug_print;
×
4234
            ++debug_cached;
×
4235
            while (true)
×
4236
            {
4237
                debug_str.clear();
×
4238
                EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0};
×
4239
                results = p.evaluate(1, results);
×
4240
                auto score = compute_score(results, p.factors);
×
4241
                if (score.points >= std::get<0>(op) && score.points <= std::get<1>(op))
×
4242
                {
4243
                    std::cout << debug_str << std::flush;
×
4244
                    print_results(results, p.factors);
×
4245
                    break;
×
4246
                }
4247
            }
×
4248
            --debug_cached;
×
4249
            --debug_print;
×
4250
            break;
×
4251
        }
4252
        }
4253
    }
4254
    write_db(prefix);
234✔
4255
    }
4256
    else {
4257
        std::cout << "No operation specified" << std::endl;
×
4258
    }
4259
    return fr;
156✔
4260
}
858✔
4261

4262
std::vector<const char *> strlist(std::vector<std::string> &input)
×
4263
{
4264
    std::vector<const char *> result;
×
4265

4266
    // remember the nullptr terminator
4267
    result.reserve(input.size() + 1);
×
4268

4269
    for (auto &i : input)
×
4270
        result.push_back(i.data());
×
4271
    result.push_back(nullptr);
×
4272
    return result;
×
4273
}
×
4274

4275
DeckResults start(int argc, const char **argv)
×
4276
{
4277
    std::vector<std::string> result_decks;
×
4278
    DeckResults drc;
×
4279
    bool first = true;
×
4280
    result_decks.push_back(argv[1]);
×
4281
    for (int j = 0; j < argc; ++j)
×
4282
    {
4283
        int add_j_inc = 0;
×
4284
        bool do_it = false;
×
4285
        std::stringstream param_file;
×
4286
        if (strcmp(argv[j], "boost-ml") == 0)
×
4287
        {
4288
            do_it = true;
×
4289
            add_j_inc = -1;
×
4290
            param_file << "noop" << std::endl
×
4291
                       << "only-ml" << std::endl
×
4292
                       << "deck @1@ no-ml" << std::endl;
×
4293
        }
4294
        if (strcmp(argv[j], "-p") == 0 || strcmp(argv[j], "params") == 0)
×
4295
        {
4296
            if (j + 1 < argc)
×
4297
            {
4298
                std::ifstream in_param_file(argv[j + 1]);
×
4299
                if (in_param_file.good())
×
4300
                {
4301
                    param_file << in_param_file.rdbuf();
×
4302
                    in_param_file.close();
×
4303
                    do_it = true;
4304
                }
4305
                else
4306
                {
4307
                    std::cout << "Error loading params file " << argv[j + 1] << std::endl;
×
4308
                }
4309
            }
×
4310
            else
4311
            {
4312
                std::cout << "-p/params needs a file as parameter" << std::endl;
×
4313
            }
4314
        }
4315
        if (do_it)
×
4316
        {
4317
            {
×
4318
                param_file.clear();
×
4319
                param_file.seekg(0, std::ios::beg);
×
4320
                // param_file to istream
4321
                std::cout << "Loading params file " << argv[j + 1] << std::endl;
×
4322
                std::string line, tmp, first_line = "";
×
4323
                std::vector<std::string> first_split, cur_split;
×
4324
                // first count parameters in file
4325
                while (param_file && !param_file.eof())
×
4326
                {
4327
                    std::getline(param_file, line);
×
4328
                    if (is_line_empty_or_commented(line))
×
4329
                        continue;
×
4330
                    for (int z = 0; z < argc - j - 1; ++z)
×
4331
                    {
4332
                        std::string rep("$" + std::to_string(z) + "$");
×
4333
                        if (line.find(rep) != std::string::npos && z > add_j_inc)
×
4334
                            add_j_inc = z;
×
4335
                    }
×
4336
                }
4337
                param_file.clear();
×
4338
                param_file.seekg(0, std::ios::beg);
×
4339
                while (param_file && !param_file.eof())
×
4340
                {
4341
                    std::getline(param_file, line);
×
4342
                    if (is_line_empty_or_commented(line))
×
4343
                        continue;
×
4344
                    // std::cout << line << std::endl;
4345
                    if (first_line == "")
×
4346
                    {
4347
                        first_line = line;
×
4348
                        std::istringstream ss(first_line);
×
4349
                        while (ss >> boost::io::quoted(tmp))
×
4350
                        {
4351
                            for (unsigned z = 0; z < result_decks.size(); ++z)
×
4352
                            {
4353
                                boost::replace_all(tmp, "@" + std::to_string(z) + "@", result_decks[z]);
×
4354
                            }
4355
                            for (int z = 0; z < argc - j - 1; ++z)
×
4356
                            {
4357
                                std::string rep("$" + std::to_string(z) + "$");
×
4358
                                boost::replace_all(tmp, rep, std::string(argv[j + 1 + z]));
×
4359
                            }
×
4360
                            first_split.push_back(tmp);
×
4361
                        }
4362
                    }
×
4363
                    else
4364
                    {
4365
                        cur_split = first_split;
×
4366
                        std::istringstream ss(line);
×
4367
                        while (ss >> boost::io::quoted(tmp))
×
4368
                        {
4369
                            for (unsigned z = 0; z < result_decks.size(); ++z)
×
4370
                            {
4371
                                boost::replace_all(tmp, "@" + std::to_string(z) + "@", result_decks[z]);
×
4372
                            }
4373
                            for (int z = 0; z < argc - j - 1; ++z)
×
4374
                            {
4375
                                std::string rep("$" + std::to_string(z) + "$");
×
4376
                                boost::replace_all(tmp, rep, std::string(argv[j + 1 + z]));
×
4377
                            }
×
4378
                            cur_split.push_back(tmp);
×
4379
                        }
4380
                        for (int i = 0; i < argc; ++i)
×
4381
                        {
4382
                            if (i < j)
×
4383
                                cur_split.insert(cur_split.begin() + i, std::string(argv[i]));
×
4384
                            if (i > j + 1 + add_j_inc)
×
4385
                                cur_split.push_back(std::string(argv[i]));
×
4386
                        }
4387
                        if (!first)
×
4388
                        {
4389
                            // replace passed deck with most recent deck
4390
                            cur_split.erase(cur_split.begin() + 1);
×
4391
                            cur_split.insert(cur_split.begin() + 1, result_decks[result_decks.size() - 1]);
×
4392
                        }
4393
                        first = false;
×
4394

4395
                        std::cout << std::endl
×
4396
                                  << "///////////////" << std::endl;
×
4397
                        // std::cout << result_decks[result_decks.size()-1] <<std::endl;
4398
                        int k = 0;
×
4399
                        for (auto &str : cur_split)
×
4400
                        {
4401
                            if (k > 0)
×
4402
                                std::cout << "\"";
×
4403
                            std::cout << str;
×
4404
                            if (k > 0)
×
4405
                                std::cout << "\"";
×
4406
                            std::cout << ' ';
×
4407
                            k++;
×
4408
                        }
4409
                        std::cout << std::endl;
×
4410
                        std::cout << "///////////////" << std::endl
×
4411
                                  << std::endl;
×
4412

4413
                        drc = start(cur_split.size(), strlist(cur_split).data());
×
4414

4415
                        // result to string
4416
                        std::stringstream oss;
×
4417
                        if (drc.first->commander)
×
4418
                            oss << drc.first->commander->m_name << ", ";
×
4419
                        if (drc.first->alpha_dominion)
×
4420
                            oss << drc.first->alpha_dominion->m_name << ", ";
×
4421
                        print_cards_inline(drc.first->cards, oss, drc.first);
×
4422
                        std::string decks(oss.str());
×
4423
                        std::replace(decks.begin(), decks.end(), '\n', ' ');
×
4424
                        result_decks.push_back(decks);
×
4425

4426
                        // print_cards_inline(drc.first->cards,std::cout);
4427
                    }
×
4428
                }
4429
                return drc;
×
4430
            }
×
4431
        }
4432
        else
4433
        {
4434
            // return run(argc,argv);
4435
        }
4436
    }
×
4437
    init();
×
4438
    auto rtrn = run(argc, argv);
×
4439
    return rtrn;
×
4440
}
×
4441

4442
#if !defined(TEST)
4443
int main(int argc, const char **argv)
4444
{
4445
#ifndef NTIMER
4446
    boost::timer::auto_cpu_timer t;
4447
#endif
4448
    if (argc == 2 && strcmp(argv[1], "-version") == 0)
4449
    {
4450
        std::cout << "Tyrant Unleashed Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl;
4451
        return 0;
4452
    }
4453
    if (argc < 2)
4454
    {
4455
        usage(argc, argv);
4456
        return 255;
4457
    }
4458
    start(argc, argv);
4459
    return 0;
4460
}
4461
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc