如何将指定Drools规则转换为OptaPlanner Constraint Streams
将Drools DRL规则转换为OptaPlanner ConstraintProvider的实现示例
核心转换思路
DRL中的accumulate逻辑对应OptaPlanner Constraint Streams的groupBy+sum操作;DRL中通过id < $leftId避免重复计数的逻辑,在Constraint Streams中可通过分组聚合后统一计算违规值替代,无需逐Game匹配,避免重复扣分。
1. 规则:eachTeamCanPlayNoOfTimesPerTime
DRL逻辑
统计同一日期、同一时间段内,某支球队(主场/客场)参与的比赛总次数(gameStaticCount求和),若超出teamPlayGamesPerTime阈值,按超出量扣硬约束分。
ConstraintProvider实现
private Constraint eachTeamCanPlayNoOfTimesPerTime(ConstraintFactory constraintFactory) { return constraintFactory.from(Game.class) // 过滤有效比赛 .filter(game -> game.getGamePeriod() != null) // 拆分主场/客场球队,避免漏统计 .flatMap(game -> Stream.of( new TeamGameTuple(game.getHomeTeam(), game), new TeamGameTuple(game.getAwayTeam(), game) )) // 按日期、时间段、球队分组,统计总比赛次数 .groupBy( tuple -> tuple.getGame().getMatchDate(), tuple -> tuple.getGame().getStartTime(), TeamGameTuple::getTeam, sum(TeamGameTuple::getGameStaticCount) ) // 过滤超出阈值的分组 .filter((matchDate, startTime, team, totalGames) -> totalGames > team.getTeamPlayGamesPerTime()) // 按超出量扣硬约束分 .penalize(HardSoftScore.ONE_HARD, (matchDate, startTime, team, totalGames) -> totalGames - team.getTeamPlayGamesPerTime()) .asConstraint("eachTeamCanPlayNoOfTimesPerTime"); } // 辅助Tuple类,关联球队与比赛 private static class TeamGameTuple { private final Team team; private final Game game; public TeamGameTuple(Team team, Game game) { this.team = team; this.game = game; } public Team getTeam() { return team; } public Game getGame() { return game; } public int getGameStaticCount() { return game.getGameStaticCount(); } }
2. 规则:maxmimumNoOfMatchesPerDay
DRL逻辑
统计非池赛/联赛的日期(isPoolOrLeague == 0)内的比赛总次数,若超出maxNoofMatchesDay阈值,扣1分硬约束分。
ConstraintProvider实现
private Constraint maximumNoOfMatchesPerDay(ConstraintFactory constraintFactory) { return constraintFactory.from(Day.class) // 过滤非池赛/联赛日期 .filter(day -> day.isPoolOrLeague() == 0) // 关联该日期下的有效比赛 .join(Game.class, Joiners.equal(Day::getMatchDate, Game::getMatchDate)) .filter((day, game) -> game.getGamePeriod() != null) // 按日期分组统计总比赛次数 .groupBy(Day::getMatchDate, sum((day, game) -> game.getGameStaticCount())) // 匹配日期实体获取阈值 .join(Day.class, Joiners.equal((matchDate, totalGames) -> matchDate, Day::getMatchDate)) .filter((matchDate, totalGames, day) -> totalGames > day.getMaxNoofMatchesDay()) // 每个违规日期扣1分硬约束 .penalize(HardSoftScore.ONE_HARD) .asConstraint("maximumNoOfMatchesPerDay"); }
3. 规则:eachTeamCanPlayNoOfTimesPerDay
DRL逻辑
统计同一日期内,某支球队(主场/客场)参与的比赛总次数,若超出teamPlayGamesPerDay阈值,按超出量扣硬约束分。
ConstraintProvider实现
private Constraint eachTeamCanPlayNoOfTimesPerDay(ConstraintFactory constraintFactory) { return constraintFactory.from(Game.class) .filter(game -> game.getGamePeriod() != null) .flatMap(game -> Stream.of( new TeamGameTuple(game.getHomeTeam(), game), new TeamGameTuple(game.getAwayTeam(), game) )) .groupBy( tuple -> tuple.getGame().getMatchDate(), TeamGameTuple::getTeam, sum(TeamGameTuple::getGameStaticCount) ) .filter((matchDate, team, totalGames) -> totalGames > team.getTeamPlayGamesPerDay()) .penalize(HardSoftScore.ONE_HARD, (matchDate, team, totalGames) -> totalGames - team.getTeamPlayGamesPerDay()) .asConstraint("eachTeamCanPlayNoOfTimesPerDay"); }
4. 规则:eachTeamCanPlayNoOfTimesPerWeek(合并原DRL的两个周规则)
DRL逻辑
统计非池赛/联赛的周内,某支球队参与的比赛总次数,若超出teamPlayGamesPerWeek阈值,按超出量扣硬约束分。原DRL拆分阈值为1和大于1的情况,Constraint Streams可统一处理。
ConstraintProvider实现
private Constraint eachTeamCanPlayNoOfTimesPerWeek(ConstraintFactory constraintFactory) { return constraintFactory.from(Game.class) .filter(game -> game.getGamePeriod() != null && game.isPoolOrLeague() == 0) // 关联比赛所属的周 .join(WeeksList.class, Joiners.lessThanOrEqual(Game::getMatchDate, WeeksList::getWeekEndDate), Joiners.greaterThanOrEqual(Game::getMatchDate, WeeksList::getWeekStartDate)) // 拆分主场/客场球队 .flatMap((game, week) -> Stream.of( new TeamWeekGameTuple(game.getHomeTeam(), week, game), new TeamWeekGameTuple(game.getAwayTeam(), week, game) )) // 按周、球队分组统计总比赛次数 .groupBy( TeamWeekGameTuple::getWeek, TeamWeekGameTuple::getTeam, sum(TeamWeekGameTuple::getGameStaticCount) ) .filter((week, team, totalGames) -> totalGames > team.getTeamPlayGamesPerWeek()) .penalize(HardSoftScore.ONE_HARD, (week, team, totalGames) -> totalGames - team.getTeamPlayGamesPerWeek()) .asConstraint("eachTeamCanPlayNoOfTimesPerWeek"); } // 辅助Tuple类,关联球队、周与比赛 private static class TeamWeekGameTuple { private final Team team; private final WeeksList week; private final Game game; public TeamWeekGameTuple(Team team, WeeksList week, Game game) { this.team = team; this.week = week; this.game = game; } public Team getTeam() { return team; } public WeeksList getWeek() { return week; } public int getGameStaticCount() { return game.getGameStaticCount(); } }
完整ConstraintProvider类结构
将上述方法整合到实现类中:
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore; import org.optaplanner.core.api.score.stream.Constraint; import org.optaplanner.core.api.score.stream.ConstraintFactory; import org.optaplanner.core.api.score.stream.ConstraintProvider; import java.util.stream.Stream; public class TournamentConstraintProvider implements ConstraintProvider { @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { return new Constraint[] { eachTeamCanPlayNoOfTimesPerTime(constraintFactory), maximumNoOfMatchesPerDay(constraintFactory), eachTeamCanPlayNoOfTimesPerDay(constraintFactory), eachTeamCanPlayNoOfTimesPerWeek(constraintFactory) }; } // 上述四个规则的实现方法... }
内容的提问来源于stack exchange,提问作者Sairam Reddy
相关产品推荐
相关产品推荐

