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 }