我正在尝试使用 cligo 生成锦标赛玩家房间分配:
player(1..20).
room(1..4).
played(1..20, 0).
rank(1..20, 1).
played(1..20, 1..20, 0).
0 { used_room(R) } 1 :- room(R).
3 { game(P, R) } 4 :- used_room(R), player(P).
:- game(P, R1), game(P, R2), R1 != R2.
penalty(Y) :- Y = #sum {
X: game(P1, R), game(P2, R), played(P1, P2, X);
X: game(P1, R), game(P2, R), rank(P1, R1), rank(P2, R2), abs(R1-R2) = X;
4 - X: played(P, X), not game(P, _)
}.
#minimize { X: penalty(X) }.
前 5 行应该是“输入”:
- 在场的玩家数量是可变的
- 可用房间的数量也是如此
- 每个玩家需要整晚打 4 局,所以我们记录了每个玩家到目前为止玩的局数
- 每个玩家都有一个排名(在联赛表中),每轮后都会更新 - 理想情况下,每个房间的玩家应该有相似的级别(想想 ELO)
- 为了阻止算法一直将相同的玩家放在一起,我们还跟踪任何给定的玩家在同一个房间里一起度过的回合数
这个想法是在每一轮之后(一旦点进入)更新这些输入,并将它们反馈给求解器以产生下一轮的分配。
然后,我尝试添加一些约束:
- 有一定数量的房间可供使用,但并非所有房间都必须使用。每个房间可以在每一轮使用或未使用
- 对于任何使用的房间,必须分配 3 或 4 名玩家(由于游戏机制 - 总是首选 4,3 用于处理边缘情况)
- 在任何给定回合中,任何玩家都不能被分配到一个以上的房间
最后,我尝试定义一些“惩罚”来指导求解器选择最佳分配:
- 对于放置在同一房间的每一对玩家 P1、P2,将 X 加到罚分中,其中 X 是他们已经一起玩过的次数。
- 对于放置在同一个房间的每一对玩家 P1、P2,将他们排名的(绝对)差异添加到罚分中。
- 对于每个还需要多打 X 轮但尚未被选中参加本轮比赛的球员,将 X 加到罚分中。
我的意思是让这个惩罚累积起来,这样每个还有 4 轮的玩家(所以每个玩家在开始时)都会在惩罚上加 4 分,而不仅仅是 1 分(这就是这段代码发生的情况)。在实践中,运行它penalty(4).
并没有任何game(player, room).
分配。
另外,我想有一些限制,这样我就不会遇到一些玩家还有几轮要玩但没有足够的玩家的情况(例如,如果你有 1、2 或 5 个玩家只需要打一轮)。我不确定正确的不变量是什么,它可以保证即使提前几轮也不会发生这种情况。这更像是一个实际的逻辑问题,而不是固执己见。在实践中,您有大约 3-4 个房间和大约 20-30 个玩家 - 重要的是,永远不能保证 # 个玩家是 4 的因素。
我目前的“实施”中缺少的其他东西是一个约束,即对于特定的玩家子集(让我们称他们为“专家”),他们中的至少一个必须留在当前回合之外(并领导它)。一般来说,对于每个使用的房间,至少有一名玩家必须留在外面(包括一名专家)。这应该是一个硬约束。
最后,我们希望最大化房间的利用率,即最大化每轮的玩家数量并最小化总体轮数。这应该是一个弱约束(就像到目前为止一起玩的排名和游戏的约束一样)。
非常感谢您的任何帮助或建议!不幸的是,文档没有提供这么多复杂的示例,所以我无法弄清楚我的用例的正确语法是什么。