File indexing completed on 2024-11-24 03:43:17

0001 /*******************************************************************
0002  *
0003  * This file is part of the KDE project "Bovo"
0004  *
0005  * Bovo is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation; either version 2, or (at your option)
0008  * any later version.
0009  *
0010  * Bovo is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU General Public License
0016  * along with Bovo; see the file COPYING.  If not, write to
0017  * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  *
0020  ********************************************************************/
0021 
0022 #include "standing.h"
0023 
0024 #include <QRandomGenerator>
0025 #include <QString>
0026 #include <array>
0027 #include <cassert>
0028 #include <cstdlib>
0029 #include <memory.h>
0030 
0031 using string = QString;
0032 
0033 // these are used by refresh() and getSuggestions()
0034 const static PatternCount suggestValues = {{0, 8, 8, 6, 5, 3}, {0, 7, 7, 4, 2, 1}};
0035 const static index_T suggestValueCount = 9;
0036 
0037 // these are used by refresh()
0038 // '0' : player's mark
0039 // '@' : wall (edge) or enemy's mark
0040 // '+' : free cell, where the player can continue the position or the enemy can block the position
0041 // '-' : free cell, where the enemy can block the position
0042 // '.' : free cell, but nobody should place here
0043 // '?' : everything but player's mark, i.e. wall, enemy or empty
0044 const static string level0[] = {QStringLiteral("00000")};
0045 const static string level1[] = {QStringLiteral(".0000+")};
0046 const static string level2[] = {QStringLiteral("@0000+"),
0047                                 QStringLiteral("+0000@"),
0048                                 QStringLiteral("00+00"),
0049                                 QStringLiteral("?000+0?"),
0050                                 QStringLiteral("?0+000?")};
0051 const static string level3[] = {QStringLiteral(".-000+."),
0052                                 QStringLiteral("-+000-@"),
0053                                 QStringLiteral("@-000+-"),
0054                                 QStringLiteral("?-00+0-"),
0055                                 QStringLiteral("@0-00+0."),
0056                                 QStringLiteral("-0+00-?"),
0057                                 QStringLiteral(".0+00-0@"),
0058                                 QStringLiteral(".0-00+0.")};
0059 const static string level4[] = {QStringLiteral("@000++"),
0060                                 QStringLiteral("++000@"),
0061                                 QStringLiteral("@0+00+0@"),
0062                                 QStringLiteral("@0+00+?"),
0063                                 QStringLiteral("?+00+0@"),
0064                                 QStringLiteral("@00+0+"),
0065                                 QStringLiteral("+0+00@"),
0066                                 QStringLiteral("@+000+@"),
0067                                 QStringLiteral("@00++0"),
0068                                 QStringLiteral("0++00@"),
0069                                 QStringLiteral("?.00++0?"),
0070                                 QStringLiteral("?0++00.?"),
0071                                 QStringLiteral("?.00++00.?"),
0072                                 QStringLiteral("?0+0+0?")};
0073 const static string level5[] = {QStringLiteral("?++00++?"),
0074                                 QStringLiteral("@-00++."),
0075                                 QStringLiteral(".++00-@"),
0076                                 QStringLiteral(".+0+0+."),
0077                                 QStringLiteral("@-0+0+."),
0078                                 QStringLiteral(".+0+0-@"),
0079                                 QStringLiteral("@0-0++0-0@"),
0080                                 QStringLiteral("@0-0++0-?"),
0081                                 QStringLiteral("?-0++0-0@"),
0082                                 QStringLiteral("?-0++0-?")};
0083 const static index_T patternTotals[heurLevels] = {1, 1, 5, 8, 14, 10};
0084 const static string *patterns[heurLevels] = {level0, level1, level2, level3, level4, level5};
0085 
0086 static bool refresh_inited = false;
0087 const static index_T max_pattern_length = 10;
0088 const static index_T patterns_total = 39;
0089 static index_T mark_inv[2][256];
0090 struct PatternData {
0091     index_T length;
0092     index_T level;
0093     mark_T mark[max_pattern_length];
0094     index_T next[4][max_pattern_length + 1];
0095 };
0096 static PatternData patternData[patterns_total];
0097 const static PatternData *patternDataEnd = &patternData[patterns_total];
0098 
0099 inline heur_T max(heur_T a, heur_T b)
0100 {
0101     return a > b ? a : b;
0102 }
0103 
0104 inline heur_T max(heur_T a, heur_T b, heur_T c)
0105 {
0106     return max(max(a, b), c);
0107 }
0108 
0109 inline heur_T min(heur_T a, heur_T b)
0110 {
0111     return a < b ? a : b;
0112 }
0113 
0114 inline heur_T min(heur_T a, heur_T b, heur_T c)
0115 {
0116     return min(min(a, b), c);
0117 }
0118 
0119 inline heur_T norm(heur_T a)
0120 {
0121     return max(-WinTreshold + 1, min(WinTreshold - 1, a));
0122 }
0123 
0124 inline count_T max(count_T a, count_T b)
0125 {
0126     return a > b ? a : b;
0127 }
0128 
0129 inline count_T min(count_T a, count_T b)
0130 {
0131     return a < b ? a : b;
0132 }
0133 
0134 void Standing::initRefresh()
0135 {
0136     if (!refresh_inited) {
0137         refresh_inited = true;
0138         mark_inv[0][mark[0]] = 0;
0139         mark_inv[0][mark[1]] = 1;
0140         mark_inv[0][mark[2]] = 2;
0141         mark_inv[0][mark[3]] = 3;
0142         mark_inv[1][mark[0]] = 1;
0143         mark_inv[1][mark[1]] = 0;
0144         mark_inv[1][mark[2]] = 2;
0145         mark_inv[1][mark[3]] = 3;
0146 
0147         index_T patternDataIndex = 0;
0148         for (index_T level = 0; level < heurLevels; ++level) {
0149             for (index_T patternIndex = 0; patternIndex < patternTotals[level]; ++patternIndex) {
0150                 patternData[patternDataIndex].length = (index_T)patterns[level][patternIndex].size();
0151                 assert(patternData[patternDataIndex].length <= max_pattern_length);
0152                 memcpy(&patternData[patternDataIndex].mark[0], patterns[level][patternIndex].toLatin1().data(), patternData[patternDataIndex].length);
0153                 patternData[patternDataIndex].level = level;
0154 
0155                 for (index_T q = 0; q <= patternData[patternDataIndex].length; ++q) {
0156                     for (index_T a = 0; a < 4; ++a) {
0157                         index_T k = min(patternData[patternDataIndex].length, q + 1);
0158                         mark_T current_mark = mark[a];
0159                         while (true) {
0160                             mark_T current_pattern = patternData[patternDataIndex].mark[k - 1];
0161                             bool good = !current_mark     ? current_pattern == '-' || current_pattern == '+' || current_pattern == '.' || current_pattern == '?'
0162                                 : current_mark == mark[0] ? current_pattern == '0'
0163                                                           : current_pattern == '@' || current_pattern == '?';
0164                             if (good)
0165                                 for (index_T i = 0; i < k - 1; ++i) {
0166                                     mark_T mark1 = patternData[patternDataIndex].mark[i];
0167                                     mark_T mark2 = patternData[patternDataIndex].mark[i + (q + 1 - k)];
0168                                     if (mark1 == '-' || mark1 == '.')
0169                                         mark1 = '+';
0170                                     if (mark2 == '-' || mark2 == '.')
0171                                         mark2 = '+';
0172                                     // I do not want to duplicate every ? into + and @ because it would slow things down
0173                                     // ? is accepted for now blindly, but extra check is needed for every match
0174                                     if (mark1 == '?' && mark2 != '0')
0175                                         mark2 = '?';
0176                                     if (mark2 == '?' && mark1 != '0')
0177                                         mark1 = '?';
0178                                     if (mark1 != mark2) {
0179                                         good = false;
0180                                         break;
0181                                     }
0182                                 }
0183                             if (good)
0184                                 break;
0185                             k--;
0186                             if (!k)
0187                                 break;
0188                         }
0189                         patternData[patternDataIndex].next[a][q] = k;
0190                     }
0191                 }
0192 
0193                 ++patternDataIndex;
0194             }
0195         }
0196         assert(patternDataIndex == patterns_total);
0197     }
0198 }
0199 
0200 Standing::Standing(pos_T _table_size_x, pos_T _table_size_y)
0201     : table_size_x(_table_size_x)
0202     , table_size_y(_table_size_y)
0203     , hval(0)
0204     , heur_seed(normal_heur_seed)
0205     , target(false)
0206     , stepCount(0)
0207     , current(0)
0208     , lastx(0)
0209     , lasty(0)
0210 {
0211     memset(table, 0, sizeof(table));
0212     memset(suggest, 0, sizeof(suggest));
0213     memset(matchCount, 0, sizeof(matchCount));
0214     memset(matchCountRow, 0, sizeof(matchCountRow));
0215     memset(matchCountColumn, 0, sizeof(matchCountColumn));
0216     memset(matchCountDiagonalSum, 0, sizeof(matchCountDiagonalSum));
0217     memset(matchCountDiagonalDiff, 0, sizeof(matchCountDiagonalDiff));
0218     memset(suggestRow, 0, sizeof(suggestRow));
0219     memset(suggestColumn, 0, sizeof(suggestColumn));
0220     memset(suggestDiagonalSum, 0, sizeof(suggestDiagonalSum));
0221     memset(suggestDiagonalDiff, 0, sizeof(suggestDiagonalDiff));
0222 }
0223 
0224 void Standing::step(pos_T x, pos_T y)
0225 {
0226     assert(x < table_size_x && y < table_size_y);
0227     table[x][y] = mark[current];
0228     current = 1 - current;
0229     lastx = x;
0230     lasty = y;
0231     ++stepCount;
0232     evaluate();
0233 }
0234 
0235 void Standing::step_server(pos_T x, pos_T y)
0236 {
0237     assert(x < table_size_x && y < table_size_y);
0238     table[x][y] = mark[2];
0239     ++stepCount;
0240     evaluate();
0241 }
0242 
0243 void Standing::posfRow(pos_T pos, int y, count_T value0, count_T value1)
0244 {
0245     pos_T x = pos - 1;
0246     assert(x < table_size_x && y < table_size_y);
0247     suggestRow[0][x][y] = max(suggestRow[0][x][y], value0);
0248     suggestRow[1][x][y] = max(suggestRow[1][x][y], value1);
0249     assert(!((suggestRow[0][x][y] || suggestRow[1][x][y]) && table[x][y]));
0250 }
0251 
0252 void Standing::posfColumn(pos_T pos, int x, count_T value0, count_T value1)
0253 {
0254     pos_T y = pos - 1;
0255     assert(x < table_size_x && y < table_size_y);
0256     suggestColumn[0][x][y] = max(suggestColumn[0][x][y], value0);
0257     suggestColumn[1][x][y] = max(suggestColumn[1][x][y], value1);
0258     assert(!((suggestColumn[0][x][y] || suggestColumn[1][x][y]) && table[x][y]));
0259 }
0260 
0261 void Standing::posfDiagonalSum(pos_T pos, int sum, count_T value0, count_T value1)
0262 {
0263     pos_T y0 = sum < table_size_x ? 0 : sum - table_size_x + 1;
0264     pos_T y = y0 + pos - 1;
0265     pos_T x = sum - y;
0266     assert(x < table_size_x && y < table_size_y);
0267     suggestDiagonalSum[0][x][y] = max(suggestDiagonalSum[0][x][y], value0);
0268     suggestDiagonalSum[1][x][y] = max(suggestDiagonalSum[1][x][y], value1);
0269     assert(!((suggestDiagonalSum[0][x][y] || suggestDiagonalSum[1][x][y]) && table[x][y]));
0270 }
0271 
0272 void Standing::posfDiagonalDiff(pos_T pos, int diff, count_T value0, count_T value1)
0273 {
0274     pos_T y0 = -diff > 0 ? -diff : 0;
0275     pos_T y = y0 + pos - 1;
0276     pos_T x = diff + y;
0277     assert(x < table_size_x && y < table_size_y);
0278     suggestDiagonalDiff[0][x][y] = max(suggestDiagonalDiff[0][x][y], value0);
0279     suggestDiagonalDiff[1][x][y] = max(suggestDiagonalDiff[1][x][y], value1);
0280     assert(!((suggestDiagonalDiff[0][x][y] || suggestDiagonalDiff[1][x][y]) && table[x][y]));
0281 }
0282 
0283 void Standing::evaluate()
0284 {
0285     countMatches();
0286     decide();
0287     if (current)
0288         hval *= -1;
0289     int current_seed = QRandomGenerator::global()->bounded(2 * (int)heur_seed + 1) - (int)heur_seed;
0290     int hval_int = (int)hval + current_seed;
0291     hval = hval_int > MaxHeur ? MaxHeur : hval_int < MinHeur ? MinHeur : hval_int;
0292 }
0293 
0294 void Standing::countMatches()
0295 {
0296     sample_T sample;
0297     sample.reserve(max(table_size_x, table_size_y) + 2);
0298 
0299     sample.push_back(mark[2]);
0300     for (pos_T x = 0; x < table_size_x; ++x) {
0301         sample.push_back(table[x][lasty]);
0302         suggestRow[0][x][lasty] = 0;
0303         suggestRow[1][x][lasty] = 0;
0304     }
0305     sample.push_back(mark[2]);
0306     refresh(sample, matchCountRow[lasty], lasty, &Standing::posfRow);
0307 
0308     sample.clear();
0309     sample.push_back(mark[2]);
0310     for (pos_T y = 0; y < table_size_y; ++y) {
0311         sample.push_back(table[lastx][y]);
0312         suggestColumn[0][lastx][y] = 0;
0313         suggestColumn[1][lastx][y] = 0;
0314     }
0315     sample.push_back(mark[2]);
0316     refresh(sample, matchCountColumn[lastx], lastx, &Standing::posfColumn);
0317 
0318     sample.clear();
0319     sample.push_back(mark[2]);
0320     int sum = lastx + lasty;
0321     assert(0 <= sum && sum < table_size_x + table_size_y - 1);
0322     pos_T y0 = sum < table_size_x ? 0 : sum - table_size_x + 1;
0323     pos_T ym = sum < table_size_y ? sum + 1 : table_size_y;
0324     assert(y0 < ym);
0325     for (pos_T y = y0; y < ym; ++y) {
0326         sample.push_back(table[sum - y][y]);
0327         suggestDiagonalSum[0][sum - y][y] = 0;
0328         suggestDiagonalSum[1][sum - y][y] = 0;
0329     }
0330     sample.push_back(mark[2]);
0331     refresh(sample, matchCountDiagonalSum[sum], sum, &Standing::posfDiagonalSum);
0332 
0333     sample.clear();
0334     sample.push_back(mark[2]);
0335     int diff = lastx - lasty;
0336     assert(-table_size_y < diff && diff < table_size_x);
0337     y0 = diff < 0 ? -diff : 0;
0338     ym = min(table_size_y, table_size_x - diff);
0339     assert(y0 < ym);
0340     for (pos_T y = y0; y < ym; ++y) {
0341         sample.push_back(table[y + diff][y]);
0342         suggestDiagonalDiff[0][y + diff][y] = 0;
0343         suggestDiagonalDiff[1][y + diff][y] = 0;
0344     }
0345     sample.push_back(mark[2]);
0346     refresh(sample, matchCountDiagonalDiff[diff + table_size_y - 1], diff, &Standing::posfDiagonalDiff);
0347 }
0348 
0349 void Standing::refresh(sample_T &sample_vect, PatternCount &local, int inv, posf_T posf)
0350 {
0351     PatternCount newCount;
0352     memset(newCount, 0, sizeof(newCount));
0353 
0354     auto sample_size = (pos_T)sample_vect.size();
0355     mark_T sample[2 * max_table_size - 1];
0356     for (pos_T i = 0; i < sample_size; ++i) {
0357         sample[i] = sample_vect[i];
0358     }
0359 
0360     const pos_T range = 3;
0361     pos_T begin_pos = 1;
0362     while (!sample[begin_pos])
0363         ++begin_pos;
0364     if (begin_pos < range)
0365         begin_pos = 0;
0366     else
0367         begin_pos -= range;
0368     int end_pos = sample_size - 2;
0369     while (!sample[end_pos])
0370         end_pos--;
0371     if (end_pos + range > sample_size - 1)
0372         end_pos = sample_size - 1;
0373     else
0374         end_pos += range;
0375 
0376     index_T correct[2][patterns_total];
0377     for (index_T i = 0; i < patterns_total; ++i) {
0378         correct[0][i] = 0;
0379         correct[1][i] = 0;
0380     }
0381 
0382     index_T player = current;
0383     do {
0384         index_T *correct_player = correct[player];
0385         index_T *mark_inv_player = mark_inv[player];
0386         for (pos_T i = begin_pos; i <= end_pos; ++i) {
0387             index_T sample_a = mark_inv_player[sample[i]];
0388             assert(sample_a < 4);
0389 
0390             index_T *current_correct = correct_player;
0391             for (PatternData *current_pattern = patternData; current_pattern != patternDataEnd; ++current_pattern) {
0392                 index_T current_correct_val = current_pattern->next[sample_a][*current_correct];
0393                 *current_correct = current_correct_val;
0394                 if (current_correct_val == current_pattern->length) {
0395                     mark_T *pattern = current_pattern->mark;
0396                     pos_T pattern_size = current_pattern->length;
0397                     index_T level = current_pattern->level;
0398                     pos_T match_start_pos = i + 1 - pattern_size;
0399 
0400                     // probably a match, but check once again because ? symbols
0401                     bool good_match = true;
0402                     for (pos_T j = 0; j < pattern_size; ++j) {
0403                         assert(match_start_pos + j < sample_size);
0404                         if ((pattern[j] == '+' || pattern[j] == '-' || pattern[j] == '.') && sample[match_start_pos + j]) {
0405                             good_match = false;
0406                             break;
0407                         }
0408                         if (pattern[j] == '@' && !sample[match_start_pos + j]) {
0409                             good_match = false;
0410                             break;
0411                         }
0412                     }
0413 
0414                     if (good_match) {
0415                         ++newCount[player][level];
0416                         for (pos_T j = 0; j < pattern_size; ++j) {
0417                             assert(match_start_pos + j < sample_size);
0418                             if (pattern[j] == '+') {
0419                                 (this->*posf)(match_start_pos + j, inv, suggestValues[player][level], suggestValues[1 - player][level]);
0420                             } else if (pattern[j] == '-') {
0421                                 (this->*posf)(match_start_pos + j, inv, player == 0 ? 0 : suggestValues[1][level], player == 1 ? 0 : suggestValues[1][level]);
0422                             }
0423                         }
0424                     }
0425                 }
0426                 ++current_correct;
0427             }
0428         }
0429         player ^= 1;
0430     } while (player != current);
0431 
0432     for (index_T k = 0; k < heurLevels; ++k) {
0433         assert(matchCount[0][k] >= local[0][k] && matchCount[1][k] >= local[1][k]);
0434         matchCount[0][k] = matchCount[0][k] + newCount[0][k] - local[0][k];
0435         matchCount[1][k] = matchCount[1][k] + newCount[1][k] - local[1][k];
0436         local[0][k] = newCount[0][k];
0437         local[1][k] = newCount[1][k];
0438         assert(matchCount[0][k] >= local[0][k] && matchCount[1][k] >= local[1][k]);
0439     }
0440 }
0441 
0442 void Standing::decide()
0443 {
0444     // I have a 5
0445     if (matchCount[current][0]) {
0446         target = true;
0447         hval = MaxHeur;
0448         return;
0449     }
0450     // the opponent has a 5
0451     if (matchCount[1 - current][0]) {
0452         target = true;
0453         hval = MinHeur;
0454         return;
0455     }
0456     // the table is full
0457     if (stepCount >= table_size_x * table_size_y) {
0458         target = true;
0459         hval = 0;
0460         return;
0461     }
0462     // I have an open or a closed 4
0463     if (matchCount[current][1] || matchCount[current][2]) {
0464         hval = MaxHeur - depth_decay;
0465         return;
0466     }
0467     // the opponent has an open 4 or two closed 4-s
0468     if (matchCount[1 - current][1] || matchCount[1 - current][2] >= 2) {
0469         hval = MinHeur + 2 * depth_decay;
0470         return;
0471     }
0472     /*
0473         0 0
0474         0 0
0475         0 1
0476         ? ?
0477         ? ?
0478         ? ?
0479     */
0480     heur_T self_attack_4 =
0481         (-90 + 40 * matchCount[current][4] + 7 * matchCount[current][5] - 10 * matchCount[1 - current][4] - 7 * matchCount[1 - current][5]) * HeurPercent;
0482     heur_T opponent_attack_4 =
0483         (90 + 10 * matchCount[current][4] + 7 * matchCount[current][5] - 40 * matchCount[1 - current][4] - 7 * matchCount[1 - current][5]) * HeurPercent;
0484     heur_T self_attack_3 =
0485         (-90 + 40 * matchCount[current][4] + 28 * matchCount[current][5] - 10 * matchCount[1 - current][4] - 7 * matchCount[1 - current][5]) * HeurPercent;
0486     heur_T opponent_attack_3 =
0487         (90 + 10 * matchCount[current][4] + 7 * matchCount[current][5] - 40 * matchCount[1 - current][4] - 28 * matchCount[1 - current][5]) * HeurPercent;
0488     heur_T self_defense =
0489         (8 + 10 * matchCount[current][4] + 7 * matchCount[current][5] - 10 * matchCount[1 - current][4] - 7 * matchCount[1 - current][5]) * HeurPercent;
0490     heur_T opponent_defense =
0491         (-8 + 10 * matchCount[current][4] + 7 * matchCount[current][5] - 10 * matchCount[1 - current][4] - 7 * matchCount[1 - current][5]) * HeurPercent;
0492 
0493     // the opponent has a 4 and a 3
0494     if (matchCount[1 - current][2] == 1 && matchCount[1 - current][3] >= 1) {
0495         hval = MinHeur + 4 * depth_decay;
0496         return;
0497     }
0498     // the opponent has a 4 and I don't have a 3
0499     if (matchCount[1 - current][2] == 1 && matchCount[current][3] == 0) {
0500         hval = norm(min(opponent_attack_4, max(opponent_attack_3, self_attack_4), max(opponent_defense, self_attack_3)));
0501         return;
0502     }
0503     // the opponent has a 4 and I have one 3
0504     if (matchCount[1 - current][2] == 1 && matchCount[current][3] == 1) {
0505         hval = norm(min(opponent_attack_4, max(self_attack_3, min(self_defense, opponent_attack_3))));
0506         return;
0507     }
0508     // the opponent has a 4 and I have two 3-s
0509     if (matchCount[1 - current][2] == 1 && matchCount[current][3] >= 2) {
0510         hval = norm(opponent_attack_4);
0511         return;
0512     }
0513     /*
0514         ? ?
0515         ? ?
0516         ? ?
0517     */
0518     // I have a 3
0519     if (matchCount[current][3] >= 1) {
0520         hval = MaxHeur - 3 * depth_decay;
0521         return;
0522     }
0523     /*
0524         0 ?
0525         ? ?
0526         ? ?
0527     */
0528     // the opponent has two 3-s and I don't have 4 possibilities
0529     if (matchCount[1 - current][3] >= 2 && matchCount[current][4] == 0) {
0530         hval = MinHeur + 4 * depth_decay;
0531         return;
0532     }
0533     // the opponent has two 3-s and I have 4 possibilities
0534     if (matchCount[1 - current][3] >= 2 && matchCount[current][4] >= 1) {
0535         hval = norm(self_attack_4);
0536         return;
0537     }
0538     /*
0539         0 1
0540         ? ?
0541         ? ?
0542     */
0543     // the opponent has a 3
0544     if (matchCount[1 - current][3] == 1) {
0545         hval = norm(max(self_attack_4, min(opponent_attack_3, max(opponent_defense, self_attack_3))));
0546         return;
0547     }
0548     /*
0549         ? ?
0550         ? ?
0551     */
0552     // I can attack
0553     hval = norm(max(self_attack_4, min(self_attack_3, opponent_attack_4), min(self_defense, opponent_attack_3)));
0554 }
0555 
0556 void Standing::getSuggestions(suggestions_T &suggestions)
0557 {
0558     // should we use smart cut-offs or play like a fool?
0559     if (heur_seed < MaxHeur) {
0560         std::array<Field, suggestValueCount> suggestPos;
0561         assert((suggestPos.fill({255, 255}), true));
0562 
0563         for (pos_T x = 0; x < table_size_x; ++x) {
0564             for (pos_T y = 0; y < table_size_y; ++y) {
0565                 count_T val = max(suggestRow[current][x][y], suggestColumn[current][x][y]);
0566                 val = max(val, suggestDiagonalSum[current][x][y]);
0567                 val = max(val, suggestDiagonalDiff[current][x][y]);
0568                 suggestPos[val] = Field(x, y);
0569                 suggest[current][x][y] = val;
0570                 assert(!(val && table[x][y]));
0571             }
0572         }
0573 
0574         suggestions.clear();
0575         // if I can win now, do it
0576         if (matchCount[current][1] || matchCount[current][2]) {
0577             assert(suggestPos[8].x != 255);
0578             suggestions.push_back(suggestPos[8]);
0579             return;
0580         }
0581         // if the opponent can win next turn, stop him
0582         if (matchCount[1 - current][1] || matchCount[1 - current][2]) {
0583             assert(suggestPos[7].x != 255);
0584             suggestions.push_back(suggestPos[7]);
0585             return;
0586         }
0587         // if I can make a clear 4, do it
0588         if (matchCount[current][3]) {
0589             assert(suggestPos[6].x != 255);
0590             suggestions.push_back(suggestPos[6]);
0591             return;
0592         }
0593 
0594         index_T treshold;
0595         // if the opponent has at least two 3-s, try creating 4-s as a last chance
0596         if (matchCount[1 - current][3] >= 2 && matchCount[current][4])
0597             treshold = 5;
0598         else
0599             // if the opponent has a 3, close it
0600             if (matchCount[1 - current][3])
0601                 treshold = 4;
0602             else
0603                 // if I can make a lot of 3-s and 4-s then attack, or destroy opponent's 4 possibilities
0604                 if (matchCount[current][4] + matchCount[current][5] >= 3)
0605                     treshold = 2;
0606                 else
0607                     // if I can make 3-s and 4-s, attack or defend
0608                     if (matchCount[current][4] || matchCount[current][5])
0609                         treshold = 1;
0610                     else
0611                         // if the opponent can make 3-s and 4-s, defend
0612                         if (matchCount[1 - current][4] || matchCount[1 - current][5])
0613                             treshold = 1;
0614                         else
0615                             // if nobody has nothing, try some close positions
0616                             treshold = 0;
0617 
0618         if (treshold <= 2) {
0619             int count = 0;
0620             // filter far away defensive positions
0621             if (treshold > 0) {
0622                 for (pos_T x = 0; x < table_size_x; ++x) {
0623                     for (pos_T y = 0; y < table_size_y; ++y) {
0624                         if (suggest[current][x][y] && suggest[current][x][y] <= 2) {
0625                             pos_T x1 = x > 0 ? x - 1 : 0;
0626                             pos_T x2 = x < table_size_x - 1 ? x + 2 : table_size_x;
0627                             pos_T y1 = y > 0 ? y - 1 : 0;
0628                             pos_T y2 = y < table_size_y - 1 ? y + 2 : table_size_y;
0629                             bool far = true;
0630                             for (pos_T xx = x1; xx < x2 && far; ++xx) {
0631                                 for (pos_T yy = y1; yy < y2 && far; ++yy) {
0632                                     if (table[xx][yy])
0633                                         far = false;
0634                                 }
0635                             }
0636                             if (far)
0637                                 suggest[current][x][y] = 0;
0638                         }
0639                         if (suggest[current][x][y])
0640                             ++count;
0641                     }
0642                 }
0643             }
0644 
0645             // if there are not enough smart positions
0646             if (count < 4) {
0647                 // check every near position
0648                 treshold = 1;
0649                 for (pos_T x = 0; x < table_size_x; ++x) {
0650                     for (pos_T y = 0; y < table_size_y; ++y) {
0651                         if (table[x][y] && table[x][y] != mark[2]) {
0652                             pos_T x1 = x > 0 ? x - 1 : 0;
0653                             pos_T x2 = x < table_size_x - 1 ? x + 2 : table_size_x;
0654                             pos_T y1 = y > 0 ? y - 1 : 0;
0655                             pos_T y2 = y < table_size_y - 1 ? y + 2 : table_size_y;
0656                             for (pos_T xx = x1; xx < x2; ++xx) {
0657                                 for (pos_T yy = y1; yy < y2; ++yy) {
0658                                     if (!table[xx][yy])
0659                                         suggest[current][xx][yy] = 1;
0660                                 }
0661                             }
0662                         }
0663                     }
0664                 }
0665             }
0666         }
0667 
0668         for (pos_T x = 0; x < table_size_x; ++x) {
0669             for (pos_T y = 0; y < table_size_y; ++y) {
0670                 if (suggest[current][x][y] >= treshold) {
0671                     suggestions.push_back(Field(x, y));
0672                 }
0673             }
0674         }
0675     } else {
0676         for (pos_T x = 0; x < table_size_x; ++x) {
0677             for (pos_T y = 0; y < table_size_y; ++y) {
0678                 if (table[x][y] && table[x][y] != mark[2]) {
0679                     pos_T x1 = x > 0 ? x - 1 : 0;
0680                     pos_T x2 = x < table_size_x - 1 ? x + 2 : table_size_x;
0681                     pos_T y1 = y > 0 ? y - 1 : 0;
0682                     pos_T y2 = y < table_size_y - 1 ? y + 2 : table_size_y;
0683                     for (pos_T xx = x1; xx < x2; ++xx) {
0684                         for (pos_T yy = y1; yy < y2; ++yy) {
0685                             if (!table[xx][yy])
0686                                 suggest[current][xx][yy] = 1;
0687                         }
0688                     }
0689                 }
0690             }
0691         }
0692 
0693         for (pos_T x = 0; x < table_size_x; ++x) {
0694             for (pos_T y = 0; y < table_size_y; ++y) {
0695                 if (suggest[current][x][y]) {
0696                     suggestions.push_back(Field(x, y));
0697                 }
0698             }
0699         }
0700     }
0701 
0702     assert(suggestions.size() > 0);
0703 }