You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将指定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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 04:17:36