I started studying Google OR-Tools and decided to try to make a task from examples, adding my own restrictions. And it’s impossible to make one of them, which determines that after the worker leaves for a shift, it should remain for a random number of days, after which one day off follows, and the next shift is issued, which should also remain for a random number of days.
The code itself:
using System;
using System.Collections.Generic;
using System.Linq;
using Google.OrTools.Sat;
public class WorkerSolutionObserver : CpSolverSolutionCallback
{
public WorkerSolutionObserver(BoolVar[,,] shifts, int num_workers, int num_days, int num_shifts,
HashSet<int> to_print, int last_solution_explored)
{
shifts_ = shifts;
num_workers_ = num_workers;
num_days_ = num_days;
num_shifts_ = num_shifts;
to_print_ = to_print;
last_solution_explored_ = last_solution_explored;
}
public override void OnSolutionCallback()
{
solution_count_++;
if (to_print_.Contains(solution_count_))
{
Console.WriteLine(String.Format("Solution #{0}: time = {1:.02} s", solution_count_, WallTime()));
for (int d = 0; d < num_days_; ++d)
{
Console.WriteLine(String.Format("Day #{0}", d+1));
for (int w = 0; w < num_workers_; ++w)
{
for (int s = 0; s < num_shifts_; ++s)
{
if (BooleanValue(shifts_[w, d, s]))
{
Console.WriteLine(String.Format(" Worker #{0} is working shift #{1}", w, s));
}
}
}
}
}
if (solution_count_ >= last_solution_explored_)
{
StopSearch();
}
}
public int SolutionCount()
{
return solution_count_;
}
private int solution_count_;
private BoolVar[,,] shifts_;
private int num_workers_;
private int num_days_;
private int num_shifts_;
private HashSet<int> to_print_;
private int last_solution_explored_;
}
public class WorkerSat
{
static void Main()
{
int num_workers = 5;
int num_shifts = 5;
int num_days = 10;
var all_workers = Enumerable.Range(0, num_workers);
var all_shifts = Enumerable.Range(0, num_shifts);
var all_working_shifts = Enumerable.Range(1, num_shifts - 1);
var all_days = Enumerable.Range(0, num_days);
CpModel model = new CpModel();
// Создает переменные расписания
// shift[w, d, s]: работник "w" работает в смену "s" в день"d".
BoolVar[,,] shift = new BoolVar[num_workers, num_days, num_shifts];
foreach (int w in all_workers)
{
foreach (int d in all_days)
{
foreach (int s in all_shifts)
{
shift[w, d, s] = model.NewBoolVar(String.Format("shift_w{0}d{1}s{2}", w, d, s));
}
}
}
// Делает задания разными на каждый день на каждую смену(гарантировано, что на все смены в d день будет назначен работник)
foreach (int d in all_days)
{
foreach (int s in all_shifts)
{
List<ILiteral> workers = new List<ILiteral>();
foreach (int w in all_workers)
{
workers.Add(shift[w, d, s]);
}
model.AddExactlyOne(workers);
}
}
//Рабочий работает 1 смену в день.
foreach (int w in all_workers)
{
foreach (int d in all_days)
{
List<ILiteral> worked = new List<ILiteral>();
foreach (int s in all_shifts)
{
worked.Add(shift[w, d, s]);
}
model.AddExactlyOne(worked);
}
}
BoolVar[,] works_shift = new BoolVar[num_workers, num_shifts];
foreach (int w in all_workers)
{
foreach (int s in all_shifts)
{
works_shift[w, s] = model.NewBoolVar(String.Format("works_shift_n{0}s{1}", w, s));
BoolVar[] worked = new BoolVar[num_days];
foreach (int d in all_days)
{
worked[d] = shift[w, d, s];
}
model.AddMaxEquality(works_shift[w, s], worked);
}
}
CpSolver solver = new CpSolver();
HashSet<int> to_print = new HashSet<int>();
to_print.Add(100);
to_print.Add(200);
WorkerSolutionObserver cb = new WorkerSolutionObserver(shift, num_workers, num_days, num_shifts, to_print, 200);
solver.StringParameters = "linearization_level:1 enumerate_all_solutions:true";
CpSolverStatus status = solver.Solve(model, cb);
Console.WriteLine("Statistics");
Console.WriteLine(String.Format("- solve status: {0}", status));
Console.WriteLine("- conflicts: " + solver.NumConflicts());
Console.WriteLine("- branches: " + solver.NumBranches());
Console.WriteLine("- wall time: " + solver.WallTime() + " ms");
Console.WriteLine("- #solutions: " + cb.SolutionCount());
}
}
Thanks in advance.