@@ -1387,18 +1387,6 @@ void GameSatSolver::FillWorldFromSolverResponse(
13871387}
13881388
13891389SolverResponse GameSatSolver::Solve (const SolverRequest& request) {
1390- switch (request.strategy ()) {
1391- case ITERATE_ROLE_ASSIGNMENTS:
1392- return SolveIteration (request);
1393- case EXPLORE_ALL_SOLUTIONS:
1394- return SolveFull (request);
1395- default :
1396- return (g_.NumMinions () > 1 && g_.GetPerspective () != STORYTELLER ?
1397- SolveIteration (request) : SolveFull (request));
1398- }
1399- }
1400-
1401- SolverResponse GameSatSolver::SolveFull (const SolverRequest& request) {
14021390 SolverResponse result;
14031391 // Making a copy to add assumptions.
14041392 const auto assumptions = CollectAssumptionLiterals (request.assumptions ());
@@ -1413,13 +1401,19 @@ SolverResponse GameSatSolver::SolveFull(const SolverRequest& request) {
14131401 WriteProtoToFile (cp_model.Build (), tmp_dir / " model.pbtxt" );
14141402 }
14151403 CpSolverResponse response;
1416- unordered_map<string, SolverResponse::World*> worlds_per_key;
14171404 map<string, int > num_worlds_per_demon;
14181405 int solutions = 0 ;
14191406 operations_research::sat::Model model;
14201407 SatParameters parameters;
14211408 parameters.set_enumerate_all_solutions (!request.stop_after_first_solution ());
14221409 parameters.set_symmetry_level (0 ); // Empirically, this is faster.
1410+ // We only care about current (and starting?) role assignments, so add them
1411+ // as key variables:
1412+ for (int i = 0 ; i < g_.NumPlayers (); ++i) {
1413+ for (Role role : AllRoles (script_)) {
1414+ parameters.add_key_variables (RoleVar (i, role, g_.CurrentTime ()).index ());
1415+ }
1416+ }
14231417 model.Add (NewSatParameters (parameters));
14241418 auto log_progress = [&](bool force) {
14251419 string progress = absl::StrFormat (" Found %d solutions " , solutions);
@@ -1440,14 +1434,10 @@ SolverResponse GameSatSolver::SolveFull(const SolverRequest& request) {
14401434 SolverResponse::World cur_world;
14411435 FillWorldFromSolverResponse (r, &cur_world);
14421436 const int demon = SolutionAliveDemon (r);
1443- const string key = cur_world.DebugString ();
1444- if (!worlds_per_key.contains (key)) {
1445- worlds_per_key[key] = result.add_worlds ();
1446- *(worlds_per_key[key]) = cur_world;
1447- const string demon_name =
1448- demon == kNoPlayer ? " <Dead player>" : g_.PlayerName (demon);
1449- num_worlds_per_demon[demon_name]++;
1450- }
1437+ *(result.add_worlds ()) = cur_world;
1438+ const string demon_name =
1439+ demon == kNoPlayer ? " <Dead player>" : g_.PlayerName (demon);
1440+ num_worlds_per_demon[demon_name]++;
14511441 log_progress (false );
14521442 if (debug_mode) {
14531443 const path sat_filename = absl::StrFormat (" sat_solution_%d" , solutions);
@@ -1466,79 +1456,6 @@ SolverResponse GameSatSolver::SolveFull(const SolverRequest& request) {
14661456 return result;
14671457}
14681458
1469- SolverResponse GameSatSolver::SolveIteration (const SolverRequest& request) {
1470- SolverResponse result;
1471- // Making a copy to add assumptions repeatedly.
1472- const auto assumptions = CollectAssumptionLiterals (request.assumptions ());
1473- CpModelBuilder model (model_.Model ());
1474- model.AddBoolAnd (assumptions);
1475- path tmp_dir = " ./tmp" ;
1476- path solution_dir = tmp_dir / " solutions" ;
1477- const bool debug_mode = request.debug_mode ();
1478- if (debug_mode) {
1479- // Create the ./tmp/solutions directory, if not present.
1480- create_directories (solution_dir);
1481- WriteProtoToFile (model.Build (), tmp_dir / " model.pbtxt" );
1482- }
1483- CpSolverResponse response;
1484- map<string, int > num_worlds_per_demon;
1485- int solutions = 0 ;
1486- while (true ) {
1487- response = operations_research::sat::Solve (model.Build ());
1488- CHECK (response.status () != CpSolverStatus::MODEL_INVALID);
1489- if (response.status () == CpSolverStatus::UNKNOWN) {
1490- LOG (WARNING) << " Solver was interrupted, returning partial solution" ;
1491- break ;
1492- }
1493- if (response.status () == CpSolverStatus::INFEASIBLE) {
1494- break ;
1495- }
1496- ++solutions;
1497- if (debug_mode) {
1498- const path resp_filename = absl::StrFormat (" resp_%d.pbtxt" , solutions);
1499- WriteProtoToFile (response, solution_dir / resp_filename);
1500- }
1501- const int demon = SolutionAliveDemon (response);
1502- SolverResponse::World *world = result.add_worlds ();
1503- FillWorldFromSolverResponse (response, world);
1504- const string demon_name =
1505- demon == kNoPlayer ? " <Dead player>" : g_.PlayerName (demon);
1506- num_worlds_per_demon[demon_name]++;
1507- vector<BoolVar> current;
1508- for (int i = 0 ; i < g_.NumPlayers (); ++i) {
1509- for (Role role : AllRoles (script_)) {
1510- const BoolVar& current_i_role = RoleVar (i, role, g_.CurrentTime ());
1511- if (SolutionBooleanValue (response, current_i_role)) {
1512- current.push_back (current_i_role);
1513- }
1514- }
1515- }
1516- string progress = absl::StrFormat (" Found %d solutions " , solutions);
1517- for (const auto & it : num_worlds_per_demon) {
1518- absl::StrAppend (
1519- &progress, absl::StrFormat (" %s: %d " , it.first , it.second ));
1520- }
1521- cout << progress << endl;
1522- if (debug_mode) {
1523- const path sat_filename = absl::StrFormat (" sat_solution_%d" , solutions);
1524- model_.WriteSatSolutionToFile (response, solution_dir / sat_filename);
1525- const path world_filename = absl::StrFormat (" world_%d.pbtxt" , solutions);
1526- WriteProtoToFile (*world, solution_dir / world_filename);
1527- }
1528- if (request.stop_after_first_solution ()) {
1529- break ;
1530- }
1531- // Limit further solutions to different role assignments.
1532- model.AddBoolOr (Not (current)); // At least one literal is different.
1533- }
1534- for (const auto & it : num_worlds_per_demon) {
1535- auto * ado = result.add_alive_demon_options ();
1536- ado->set_name (it.first );
1537- ado->set_count (it.second );
1538- }
1539- return result;
1540- }
1541-
15421459// Solves the game and returns all valid worlds.
15431460SolverResponse Solve (const GameState& g) {
15441461 return GameSatSolver (g).Solve ();
0 commit comments