int quiescent(app_t *app, cnodeptr_t parent, int alpha, int beta) { int i, score = -MATE; node_t node; int delta = 200; init_node(&node); assert(app); app->search.nodes++; /* max depth */ if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) { return evaluate(app->game.board); } /* draws */ if (repetitions(app) || (app->game.board->half >= 100)) { return 0; } score = evaluate(app->game.board); if (score >= beta) return beta; /* delta pruning based on a material value of delta (this should be disabled in the endgame) */ /* The idea here is that, even if our score can improve alpha, it doesn't improve it by a significant amount, so don't bother searching these nodes */ if (score < (alpha - delta)) return alpha; if (score > alpha) alpha = score; /* generate moves (with separate captures) */ generate_moves(app->game.board, &node.ml, &node.cl); for (i = 0; i < node.cl.count; i++) { /* get the next move ordered */ next_move(i, &node.cl); if (!(do_move(app->game.board, &app->game.undo, node.cl.moves[i]))) continue; score = -quiescent(app, &node, -beta, -alpha); node.made++; undo_move(app->game.board, &app->game.undo); if (score > alpha) { if (score >= beta) { return beta; } /* update alpha */ alpha = score; } } return alpha; }
// -------------------------------------------------------------------------- // // void Test_Configuration::testPerformProcess() { // Setup a realistic system. std::vector< std::vector<double> > basis(3, std::vector<double>(3,0.0)); basis[1][0] = 0.25; basis[1][1] = 0.25; basis[1][2] = 0.25; basis[2][0] = 0.75; basis[2][1] = 0.75; basis[2][2] = 0.75; std::vector<int> basis_sites(3); basis_sites[0] = 0; basis_sites[1] = 1; basis_sites[2] = 2; std::vector<std::string> basis_elements(3); basis_elements[0] = "A"; basis_elements[1] = "B"; basis_elements[2] = "B"; // Make a 37x18x19 structure. const int nI = 37; const int nJ = 18; const int nK = 19; const int nB = 3; // Coordinates and elements. std::vector<std::vector<double> > coordinates; std::vector<std::string> elements; for (int i = 0; i < nI; ++i) { for (int j = 0; j < nJ; ++j) { for (int k = 0; k < nK; ++k) { for (int b = 0; b < nB; ++b) { std::vector<double> c(3); c[0] = i + basis[b][0]; c[1] = j + basis[b][1]; c[2] = k + basis[b][2]; coordinates.push_back(c); elements.push_back(basis_elements[b]); } } } } elements[0] = "V"; elements[216] = "V"; // These affects process 0,1 and 3 elements[1434] = "V"; elements[2101] = "V"; // This affects process 0,1 and 2 // Possible types. std::map<std::string, int> possible_types; possible_types["*"] = 0; possible_types["A"] = 1; possible_types["B"] = 2; possible_types["V"] = 3; // Setup the configuration. Configuration configuration(coordinates, elements, possible_types); // Setup the lattice map. std::vector<int> repetitions(3); repetitions[0] = nI; repetitions[1] = nJ; repetitions[2] = nK; std::vector<bool> periodicity(3, true); LatticeMap lattice_map(nB, repetitions, periodicity); // Init the match lists. configuration.initMatchLists(lattice_map, 1); // Get a process that finds a V between two B and turns one of // the Bs into an A. std::vector<std::string> process_elements1(3); process_elements1[0] = "V"; process_elements1[1] = "B"; process_elements1[2] = "B"; std::vector<std::string> process_elements2(3); process_elements2[0] = "B"; process_elements2[1] = "A"; process_elements2[2] = "B"; std::vector<std::vector<double> > process_coordinates(3, std::vector<double>(3, 0.0)); process_coordinates[1][0] = -0.25; process_coordinates[1][1] = -0.25; process_coordinates[1][2] = -0.25; process_coordinates[2][0] = 0.25; process_coordinates[2][1] = 0.25; process_coordinates[2][2] = 0.25; const double rate = 13.7; Configuration c1(process_coordinates, process_elements1, possible_types); Configuration c2(process_coordinates, process_elements2, possible_types); Process p(c1, c2, rate, basis_sites); // Now, add index 1434 to the process. // We know by construction that these match. p.addSite(1434, 0.0); // For site 1434 // 350 changes from 1 to 0 // 1434 changes from 2 to 1 // All other must remain unchanged. CPPUNIT_ASSERT_EQUAL( configuration.types()[1434], 3 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[350], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[1433], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[349], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[351], 1 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[2517], 1 ); // Peform the process. configuration.performProcess(p, 1434); // Check that the types were correctly updated. CPPUNIT_ASSERT_EQUAL( configuration.types()[1434], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[350], 1 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[1433], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[349], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[351], 1 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[2517], 1 ); // Check that the correct indices were added to the list of affected. const std::vector<int> affected = p.affectedIndices(); CPPUNIT_ASSERT_EQUAL( affected[0], 1434 ); CPPUNIT_ASSERT_EQUAL( affected[1], 350 ); }
// -------------------------------------------------------------------------- // // void Test_Configuration::testMatchLists() { // Setup a configuration. std::vector< std::vector<double> > basis(3, std::vector<double>(3,0.0)); basis[1][0] = 0.25; basis[1][1] = 0.25; basis[1][2] = 0.25; basis[2][0] = 0.75; basis[2][1] = 0.75; basis[2][2] = 0.75; std::vector<int> basis_sites(3); basis_sites[0] = 0; basis_sites[1] = 1; basis_sites[2] = 2; std::vector<std::string> basis_elements(3); basis_elements[0] = "A"; basis_elements[1] = "B"; basis_elements[2] = "B"; // Make a 37x18x19 structure. const int nI = 37; const int nJ = 18; const int nK = 19; const int nB = 3; // Coordinates and elements. std::vector<std::vector<double> > coordinates; std::vector<std::string> elements; for (int i = 0; i < nI; ++i) { for (int j = 0; j < nJ; ++j) { for (int k = 0; k < nK; ++k) { for (int b = 0; b < nB; ++b) { std::vector<double> c(3); c[0] = i + basis[b][0]; c[1] = j + basis[b][1]; c[2] = k + basis[b][2]; coordinates.push_back(c); elements.push_back(basis_elements[b]); } } } } elements[0] = "V"; elements[216] = "V"; elements[1434] = "V"; elements[2101] = "V"; // Possible types. std::map<std::string, int> possible_types; possible_types["*"] = 0; possible_types["A"] = 1; possible_types["B"] = 2; possible_types["V"] = 3; // Setup the configuration. Configuration configuration(coordinates, elements, possible_types); // Setup the lattice map. std::vector<int> repetitions(3); repetitions[0] = nI; repetitions[1] = nJ; repetitions[2] = nK; std::vector<bool> periodicity(3, true); LatticeMap lattice_map(nB, repetitions, periodicity); // Try to access the match lists before initialization. They should be // empty. CPPUNIT_ASSERT( configuration.minimalMatchList(10).empty() ); CPPUNIT_ASSERT( configuration.minimalMatchList(2101).empty() ); CPPUNIT_ASSERT( configuration.minimalMatchList(1434).empty() ); // Init the match lists. configuration.initMatchLists(lattice_map, 1); // This did something. CPPUNIT_ASSERT( !configuration.minimalMatchList(10).empty() ); CPPUNIT_ASSERT( !configuration.minimalMatchList(2101).empty() ); CPPUNIT_ASSERT( !configuration.minimalMatchList(1434).empty() ); // Get the match list the hard way. const std::vector<MinimalMatchListEntry> ref_1434 = \ configuration.minimalMatchList( 1434, lattice_map.neighbourIndices(1434), lattice_map); // Check the size. CPPUNIT_ASSERT_EQUAL( static_cast<int>(ref_1434.size()), static_cast<int>(configuration.minimalMatchList(1434).size()) ); // Check the values. for (size_t i = 0; i < ref_1434.size(); ++i) { CPPUNIT_ASSERT_EQUAL( ref_1434[i].match_type, configuration.minimalMatchList(1434)[i].match_type ); CPPUNIT_ASSERT_EQUAL( ref_1434[i].update_type, configuration.minimalMatchList(1434)[i].update_type ); CPPUNIT_ASSERT_EQUAL( ref_1434[i].index, configuration.minimalMatchList(1434)[i].index ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref_1434[i].distance, configuration.minimalMatchList(1434)[i].distance, 1.0e-14 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref_1434[i].coordinate.x(), configuration.minimalMatchList(1434)[i].coordinate.x(), 1.0e-14 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref_1434[i].coordinate.y(), configuration.minimalMatchList(1434)[i].coordinate.y(), 1.0e-14 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref_1434[i].coordinate.z(), configuration.minimalMatchList(1434)[i].coordinate.z(), 1.0e-14 ); } // Setup a process that changes V to B. // Get a process that finds a V between two B and turns one of // the Bs into an A. std::vector<std::string> process_elements1(3); process_elements1[0] = "V"; process_elements1[1] = "B"; process_elements1[2] = "B"; std::vector<std::string> process_elements2(3); process_elements2[0] = "B"; process_elements2[1] = "A"; process_elements2[2] = "B"; std::vector<std::vector<double> > process_coordinates(3, std::vector<double>(3, 0.0)); process_coordinates[1][0] = -0.25; process_coordinates[1][1] = -0.25; process_coordinates[1][2] = -0.25; process_coordinates[2][0] = 0.25; process_coordinates[2][1] = 0.25; process_coordinates[2][2] = 0.25; const double rate = 13.7; Configuration c1(process_coordinates, process_elements1, possible_types); Configuration c2(process_coordinates, process_elements2, possible_types); Process p(c1, c2, rate, basis_sites); // Now, add index 1434 to the process. // We know by construction that these match. p.addSite(1434, 0.0); // For site 1434 // 350 changes from 1 to 0 // 1434 changes from 2 to 1 // All other must remain unchanged. CPPUNIT_ASSERT_EQUAL( configuration.types()[1434], 3 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[350], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[1433], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[349], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[351], 1 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[2517], 1 ); // Peform the process. configuration.performProcess(p, 1434); // Check that the types were correctly updated. CPPUNIT_ASSERT_EQUAL( configuration.types()[1434], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[350], 1 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[1433], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[349], 2 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[351], 1 ); CPPUNIT_ASSERT_EQUAL( configuration.types()[2517], 1 ); // Check that updating the matchlist gets us the correct values. configuration.updateMatchList(1434); // Reference. const std::vector<MinimalMatchListEntry> ref2_1434 = \ configuration.minimalMatchList( 1434, lattice_map.neighbourIndices(1434), lattice_map); // Check the size. CPPUNIT_ASSERT_EQUAL( static_cast<int>(ref2_1434.size()), static_cast<int>(configuration.minimalMatchList(1434).size()) ); // Check the values. for (size_t i = 0; i < ref2_1434.size(); ++i) { CPPUNIT_ASSERT_EQUAL( ref2_1434[i].match_type, configuration.minimalMatchList(1434)[i].match_type ); CPPUNIT_ASSERT_EQUAL( ref2_1434[i].update_type, configuration.minimalMatchList(1434)[i].update_type ); CPPUNIT_ASSERT_EQUAL( ref2_1434[i].index, configuration.minimalMatchList(1434)[i].index ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref2_1434[i].distance, configuration.minimalMatchList(1434)[i].distance, 1.0e-14 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref2_1434[i].coordinate.x(), configuration.minimalMatchList(1434)[i].coordinate.x(), 1.0e-14 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref2_1434[i].coordinate.y(), configuration.minimalMatchList(1434)[i].coordinate.y(), 1.0e-14 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( ref2_1434[i].coordinate.z(), configuration.minimalMatchList(1434)[i].coordinate.z(), 1.0e-14 ); } }
int alpha_beta(app_t *app, cnodeptr_t parent, int alpha, int beta, int depth) { int palpha = alpha; int i, score = -MATE, highest = -MATE; node_t node; move_t cutoff = 0; piece_t p; init_node(&node); assert(app); app->search.nodes++; node.depth = depth; /* max depth */ if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) { /* return evaluate(app->board); */ return quiescent(app, parent, alpha, beta); } /* recursive base */ if (depth == 0) { return evaluate(app->game.board); } /* draws */ if (repetitions(app) || (app->game.board->half >= 100)) { return 0; } /* if we are checked, set the nodes checked flag */ if (check(app->game.board, app->game.board->side)) { node.flags |= NODE_CHECK; /* extend our search by 1 depth if we are in check */ /* NOTES: we may want to NOT extend our search here if the parent is in check, because the means we already extended once */ depth++; } /* TODO: - NULL moves - Late-move reduction - Tactical extensions (pins & forks -> depth++) */ /* probe our table */ if (probe_hash(&app->hash, app->game.board, &cutoff, &score, depth, alpha, beta) == TRUE) { app->hash.cut++; return score; } /* generate moves */ generate_moves(app->game.board, &node.ml, &node.ml); /* reset score */ score = -MATE; /* try to match our hash hit move */ if (cutoff != 0) { for (i = 0; i < node.ml.count; i++) { if (node.ml.moves[i] == cutoff) { node.ml.scores[i] = 20000; break; } } } /* search negamax */ for (i = 0; i < node.ml.count; i++) { /* get the next move ordered */ next_move(i, &node.ml); if (!(do_move(app->game.board, &app->game.undo, node.ml.moves[i]))) continue; score = -alpha_beta(app, &node, -beta, -alpha, depth - 1); node.made++; undo_move(app->game.board, &app->game.undo); /* score whatever is best so far */ if (score > highest) { node.best = node.ml.moves[i]; highest = score; /* update alpha */ if (score > alpha) { if (score >= beta) { /* non-captures causing beta cutoffs (killers) */ if (!is_capture(node.ml.moves[i])) { app->game.board->killers[1][app->game.board->ply] = app->game.board->killers[0][app->game.board->ply]; app->game.board->killers[0][app->game.board->ply] = node.ml.moves[i]; } /* store this beta in our transposition table */ store_hash(&app->hash, app->game.board, node.best, beta, HASH_BETA, depth); return beta; } /* update alpha */ alpha = score; /* update our history */ if (!is_capture(node.best)) { p = app->game.board->pos.squares[move_from(node.best)]; app->game.board->history[piece_color(p)][piece_type(p)][move_to(node.best)] += depth; } } } } /* check for checkmate or stalemate */ if (!node.made) { if (node.flags & NODE_CHECK) { return -MATE + app->game.board->ply; } else { return 0; } } if (alpha != palpha) { /* store this as an exact, since we beat alpha */ store_hash(&app->hash, app->game.board, node.best, highest, HASH_EXACT, depth); } else { /* store the current alpha */ store_hash(&app->hash, app->game.board, node.best, alpha, HASH_ALPHA, depth); } return alpha; }