Dinner party
パーティの席決めです。様々な条件を考慮して各テーブルに参加者を最適な組み合わせで配置する。。。そう、結婚式で一度だけ経験しましたよ。
このサンプルでは、男女が交互に並ぶ、隣同士が共通の趣味を持つ、1テーブルに職業を適正なバランスで配置する、という制約が与えられています。
- DinnerParty : @PlanningSolution
- List
getSeatList() : @ValueRangeProvider(id = "seatRange") - List
getSeatDesignationList() : @PlanningEntityCollectionProperty
- List
- SeatDesignation : @PlanningEntity
- Seat getSeat() : @PlanningVariable(valueRangeProviderRefs = {"seatRange"})
- Guest guest
SeatDesignation は Guest と Seat の組み合わせを保持し、「誰がどの席に座るか」を示すエンティティ。比較的単純ですね。SeatDesignation は 1-1 で固定の Guest を保持する(variable ではない)ため、Guest を PlanningEntity にするようなモデリングもアリなはず。Gender、Job、JobType、Hobby、HobbyPractician は、Guest に関連するクラス。モデリングの際に、比較的細かい粒度でクラス化されている印象。おそらくクラスにした方がルールが書きやすいからだろう。
ルールの1例。
rule "twoSameJobTypePerTable" when $jobType : JobType() $table : Table() not ( SeatDesignation(guestJobType == $jobType, seatTable == $table, $leftId : id, $firstJob : guestJob) and SeatDesignation(guestJobType == $jobType, seatTable == $table, id > $leftId, differentKindIfNeeded($firstJob)) ) then scoreHolder.addConstraintMatch(kcontext, -100); end
あれ?そういえばなんで JobType みたいなファクトが insert されるのだろう。どれがルールで使用するファクトだって明示してたっけ?
getProblemFacts() でした。ルール以外のスコアリング方法の時は必要無いそうです。
この example は DRL だけじゃなく、XSL(dinnerPartyExtraScoreRules.xls)にも追加の細かい席決めルールが記述されています。「DemocratとRepublicanは隣にしてはダメ」とか。dinnerPartyExtraScoreRules.xls の読み込み指定は dinnerPartySolverConfig.xml に一行追加するだけ。
<scoreDirectorFactory> <scoreDefinitionType>SIMPLE</scoreDefinitionType> <scoreDrl>org/optaplanner/examples/dinnerparty/solver/dinnerPartyScoreRules.drl</scoreDrl> <scoreDrl>org/optaplanner/examples/dinnerparty/solver/dinnerPartyExtraScoreRules.xls</scoreDrl> </scoreDirectorFactory>