| 388 | | /* Test all the moves to see whether they can owl-attack on |
| 389 | | * a large scale . Tested moves are |
| | 388 | |
| | 389 | /* Try whether the move at (pos) for (color) is also an owl attack on |
| | 390 | * (target). (dist) is the distance to the dragon, and is used for a |
| | 391 | * safety heuristic: distant moves are only accepted if they kill within |
| | 392 | * few owl nodes. |
| | 393 | */ |
| | 394 | static void |
| | 395 | try_large_scale_owl_attack(int pos, int color, int target, int dist) |
| | 396 | { |
| | 397 | int owl_nodes_before; |
| | 398 | int owl_nodes_used; |
| | 399 | int old_node_limit; |
| | 400 | int new_node_limit; |
| | 401 | int kworm = NO_MOVE; |
| | 402 | int acode; |
| | 403 | int save_verbose = verbose; |
| | 404 | |
| | 405 | ASSERT1(board[target] == OTHER_COLOR(color), pos); |
| | 406 | ASSERT1(!owl_attack_move_reason_known(pos, target), pos); |
| | 407 | DEBUG(DEBUG_LARGE_SCALE, "Trying large scale move %1m on %1m\n", pos, target); |
| | 408 | |
| | 409 | /* To avoid horizon effects, we temporarily increase |
| | 410 | * the depth values to find the large scale attacks. |
| | 411 | */ |
| | 412 | increase_depth_values(); |
| | 413 | |
| | 414 | /* To reduce the amount of aji allowed for large scale |
| | 415 | * attacks, we reduce the owl limit to 350 nodes for |
| | 416 | * attacks at distance <= 1, and 150 nodes for attacks at |
| | 417 | * distance >= 2. |
| | 418 | */ |
| | 419 | if (dist <= 1) |
| | 420 | new_node_limit = gg_min(350, owl_node_limit); |
| | 421 | else |
| | 422 | new_node_limit = gg_min(150, owl_node_limit); |
| | 423 | change_owl_node_limit(new_node_limit, &old_node_limit); |
| | 424 | |
| | 425 | if (verbose > 0) |
| | 426 | verbose--; |
| | 427 | |
| | 428 | owl_nodes_before = get_owl_node_counter(); |
| | 429 | acode = owl_does_attack(pos, target, &kworm); |
| | 430 | owl_nodes_used = get_owl_node_counter() - owl_nodes_before; |
| | 431 | |
| | 432 | if (acode >= DRAGON2(target).owl_attack_code |
| | 433 | && acode == WIN) { |
| | 434 | add_owl_attack_move(pos, target, kworm, acode); |
| | 435 | DEBUG(DEBUG_LARGE_SCALE | DEBUG_MOVE_REASONS, |
| | 436 | "Move at %1m owl-attacks %1m on a large scale(%s).\n", |
| | 437 | pos, target, result_to_string(acode)); |
| | 438 | } |
| | 439 | else |
| | 440 | DEBUG(DEBUG_LARGE_SCALE, |
| | 441 | "Move at %1m isn't a clean large scale attack on %1m (%s).\n", |
| | 442 | pos, target, result_to_string(acode)); |
| | 443 | |
| | 444 | DEBUG(DEBUG_LARGE_SCALE, " owl nodes used = %d, dist = %d\n", |
| | 445 | owl_nodes_used, dist); |
| | 446 | /* Restore settings. */ |
| | 447 | verbose = save_verbose; |
| | 448 | change_owl_node_limit(old_node_limit, NULL); |
| | 449 | decrease_depth_values(); |
| | 450 | } |
| | 451 | |
| | 452 | |
| | 453 | #define MAXIMUM_LARGE_SCALE_DIST 3 |
| | 454 | |
| | 455 | /* Test all the moves to see whether they can owl-attack a specific |
| | 456 | * dragon on a large scale . Tested moves are |
| 413 | | if (debug & DEBUG_LARGE_SCALE) |
| 414 | | gprintf("\nTrying to find large scale attack moves.\n"); |
| 415 | | |
| 416 | | /* Identify the unstable dragons and store them in a list. */ |
| 417 | | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 418 | | if (IS_STONE(board[pos]) |
| 419 | | && dragon[pos].origin == pos |
| 420 | | && board[pos] == other) |
| 421 | | if (dragon[pos].status == CRITICAL |
| 422 | | || DRAGON2(pos).owl_status == CRITICAL) { |
| 423 | | |
| 424 | | unstable_dragons[N] = pos; |
| 425 | | |
| 426 | | if (debug & DEBUG_LARGE_SCALE) |
| 427 | | gprintf("Small critical dragon found at %1m\n", pos); |
| 428 | | |
| 429 | | /* Find the physical extension of the dragon, and remember it |
| 430 | | * FIXME : probably there should be a better place to calculate |
| 431 | | * them, maybe a new field in the dragon2 array ? |
| 432 | | */ |
| 433 | | x = I(pos); |
| 434 | | y = J(pos); |
| 435 | | x_min_dragon[N] = x_max_dragon[N] = x; |
| 436 | | y_min_dragon[N] = y_max_dragon[N] = y; |
| 437 | | for (dx = 0; dx < board_size; dx++) |
| 438 | | for (dy = 0; dy < board_size; dy++) { |
| 439 | | |
| 440 | | if (ON_BOARD2(dx, dy) |
| 441 | | && is_same_dragon(pos, POS(dx, dy))) { |
| 442 | | if (dx < x_min_dragon[N]) |
| 443 | | x_min_dragon[N] = dx; |
| 444 | | if (dx > x_max_dragon[N]) |
| 445 | | x_max_dragon[N] = dx; |
| 446 | | if (dy < y_min_dragon[N]) |
| 447 | | y_min_dragon[N] = dy; |
| 448 | | if (dy > y_max_dragon[N]) |
| 449 | | y_max_dragon[N] = dy; |
| 450 | | } |
| 451 | | } |
| 452 | | N++; |
| | 475 | /* Find the physical extension of the dragon. */ |
| | 476 | for (x = 0; x < board_size; x++) |
| | 477 | for (y = 0; y < board_size; y++) { |
| | 478 | if (is_same_dragon(target, POS(x, y))) { |
| | 479 | if (x < x_min) |
| | 480 | x_min = x; |
| | 481 | if (x > x_max) |
| | 482 | x_max = x; |
| | 483 | if (y < y_min) |
| | 484 | y_min = y; |
| | 485 | if (y > y_max) |
| | 486 | y_max = y; |
| 460 | | for (k = 0; k < N; k++) |
| 461 | | for (dist = 0; dist <= maximum_distance; dist++) |
| 462 | | for (x = x_min_dragon[k]-dist; x <= x_max_dragon[k]+dist; x++) |
| 463 | | for (y = y_min_dragon[k]-dist; y <= y_max_dragon[k]+dist; y++) { |
| 464 | | target = unstable_dragons[k]; |
| 465 | | pos = POS(x, y); |
| 466 | | |
| 467 | | if (ON_BOARD2(x, y) && ON_BOARD1(pos) && (board[pos] == EMPTY)) { |
| 468 | | int a, b; |
| 469 | | a = abs(x - x_min_dragon[k]); |
| 470 | | b = abs(x - x_max_dragon[k]); |
| 471 | | dx = gg_min(a, b); |
| 472 | | a = abs(y - y_min_dragon[k]); |
| 473 | | b = abs(y - y_max_dragon[k]); |
| 474 | | dy = gg_min(a, b); |
| 475 | | |
| 476 | | if (gg_max(dx, dy) == dist) { /* maximum Manhatan distance */ |
| 477 | | int has_move_reasons = 0; |
| 478 | | int worth_trying = 0; |
| 479 | | |
| 480 | | /* See if that move has other move reasons */ |
| 481 | | has_move_reasons = 0; |
| 482 | | for (a = 0; a < MAX_REASONS; a++) { |
| 483 | | int r = move[pos].reason[a]; |
| 484 | | if (r < 0) |
| 485 | | break; |
| 486 | | |
| 487 | | has_move_reasons = 1; |
| 488 | | } |
| 489 | | |
| 490 | | |
| 491 | | /* We try all large scale attacks on small dragons (and |
| 492 | | * only moves with other move reason). |
| 493 | | */ |
| 494 | | if (board[target] == other) |
| 495 | | worth_trying = (dragon[target].size <= 6 |
| 496 | | && has_move_reasons); |
| 497 | | |
| 498 | | if (worth_trying) { |
| 499 | | int owl_nodes_before; |
| 500 | | int owl_nodes_used; |
| 501 | | int old_node_limit; |
| 502 | | int new_node_limit; |
| 503 | | |
| 504 | | if (debug & DEBUG_LARGE_SCALE) |
| 505 | | gprintf("Trying large scale move %1m on %1m\n", pos, target); |
| 506 | | |
| 507 | | /* To avoid horizon effects, we temporarily increase |
| 508 | | * the depth values to find the large scale attacks. |
| 509 | | */ |
| 510 | | increase_depth_values(); |
| 511 | | |
| 512 | | /* To reduce the amount of aji allowed for large scale |
| 513 | | * attacks, we reduce the owl limit to 350 nodes for |
| 514 | | * attacks at distance <= 1, and 150 nodes for attacks at |
| 515 | | * distance >= 2. |
| 516 | | */ |
| 517 | | if (dist <= 1) |
| 518 | | new_node_limit = gg_min(350, owl_node_limit); |
| 519 | | else |
| 520 | | new_node_limit = gg_min(150, owl_node_limit); |
| 521 | | change_owl_node_limit(new_node_limit, &old_node_limit); |
| 522 | | |
| 523 | | /* Try large scale killing moves on opponent's stones. */ |
| 524 | | owl_nodes_before = get_owl_node_counter(); |
| 525 | | if (board[target] == other |
| 526 | | && !owl_attack_move_reason_known(pos, target)) { |
| 527 | | int kworm = NO_MOVE; |
| 528 | | int acode; |
| 529 | | int save_verbose = verbose; |
| 530 | | if (verbose > 0) |
| 531 | | verbose--; |
| 532 | | |
| 533 | | acode = owl_does_attack(pos, target, &kworm); |
| 534 | | |
| 535 | | owl_nodes_used = get_owl_node_counter() - owl_nodes_before; |
| 536 | | |
| 537 | | if (acode >= DRAGON2(target).owl_attack_code |
| 538 | | && acode == WIN) { |
| 539 | | add_owl_attack_move(pos, target, kworm, acode); |
| 540 | | if (debug & DEBUG_LARGE_SCALE) |
| 541 | | gprintf("Move at %1m owl-attacks %1m on a large scale(%s).\n", |
| 542 | | pos, target, result_to_string(acode)); |
| 543 | | } |
| 544 | | else if (debug & DEBUG_LARGE_SCALE) |
| 545 | | gprintf("Move at %1m isn't a clean large scale attack on %1m (%s).\n", pos, target, result_to_string(acode)); |
| 546 | | |
| 547 | | if (debug & DEBUG_LARGE_SCALE) |
| 548 | | gprintf(" owl nodes used = %d, dist = %d\n", |
| 549 | | owl_nodes_used, dist); |
| 550 | | verbose = save_verbose; |
| 551 | | } |
| 552 | | |
| 553 | | /* Restore owl node limit */ |
| 554 | | change_owl_node_limit(old_node_limit, NULL); |
| 555 | | |
| 556 | | /* Restore the depth values */ |
| 557 | | decrease_depth_values(); |
| 558 | | } |
| 559 | | } |
| 560 | | } |
| 561 | | } |
| | 496 | for (dist = 0; dist <= MAXIMUM_LARGE_SCALE_DIST; dist++) |
| | 497 | for (x = gg_max(x_min - dist, 0); |
| | 498 | x <= gg_min(x_max + dist, board_size - 1); x++) |
| | 499 | for (y = gg_max(y_min - dist, 0); |
| | 500 | y <= gg_min(y_max + dist, board_size - 1); y++) { |
| | 501 | int pos = POS(x, y); |
| | 502 | ASSERT1(ON_BOARD2(x, y), pos); |
| | 503 | |
| | 504 | if (board[pos] == EMPTY) { |
| | 505 | int a, b, dx, dy; |
| | 506 | a = abs(x - x_min); |
| | 507 | b = abs(x - x_max); |
| | 508 | dx = gg_min(a, b); |
| | 509 | a = abs(y - y_min); |
| | 510 | b = abs(y - y_max); |
| | 511 | dy = gg_min(a, b); |
| | 512 | |
| | 513 | if (gg_max(dx, dy) == dist |
| | 514 | && move[pos].reason[0] >= 0 |
| | 515 | && !owl_attack_move_reason_known(pos, target)) |
| | 516 | /* Maximum Manhatan distance, move reason known but no owl |
| | 517 | * attack yet. |
| | 518 | */ |
| | 519 | try_large_scale_owl_attack(pos, color, target, dist); |
| | 520 | |
| | 521 | } |
| | 522 | } |