Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
import static ai.timefold.solver.core.api.score.stream.ConstraintCollectors.countBi;
import static ai.timefold.solver.core.api.score.stream.ConstraintCollectors.max;
import static ai.timefold.solver.core.api.score.stream.ConstraintCollectors.min;
import static ai.timefold.solver.core.api.score.stream.Joiners.equal;
import static ai.timefold.solver.core.api.score.stream.Joiners.filtering;
import static ai.timefold.solver.core.api.score.stream.Joiners.greaterThan;
import static ai.timefold.solver.core.api.score.stream.Joiners.lessThan;
import static ai.timefold.solver.core.api.score.stream.Joiners.overlapping;
import static ai.timefold.solver.core.api.score.stream.Joiners.*;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wildcard static imports can reduce code clarity and IDE support. Consider using explicit imports for each Joiner method (equal, greaterThan, lessThan, overlapping, filtering, contain, containedIn, containAny) to improve code maintainability and make dependencies more explicit.

Suggested change
import static ai.timefold.solver.core.api.score.stream.Joiners.*;
import static ai.timefold.solver.core.api.score.stream.Joiners.equal;
import static ai.timefold.solver.core.api.score.stream.Joiners.greaterThan;
import static ai.timefold.solver.core.api.score.stream.Joiners.lessThan;
import static ai.timefold.solver.core.api.score.stream.Joiners.overlapping;
import static ai.timefold.solver.core.api.score.stream.Joiners.filtering;
import static ai.timefold.solver.core.api.score.stream.Joiners.contain;
import static ai.timefold.solver.core.api.score.stream.Joiners.containedIn;
import static ai.timefold.solver.core.api.score.stream.Joiners.containAny;

Copilot uses AI. Check for mistakes.
import static java.util.stream.Collectors.joining;
import static org.acme.conferencescheduling.domain.ConferenceConstraintProperties.AUDIENCE_LEVEL_DIVERSITY;
import static org.acme.conferencescheduling.domain.ConferenceConstraintProperties.AUDIENCE_TYPE_DIVERSITY;
Expand Down Expand Up @@ -151,8 +147,8 @@ Constraint speakerUnavailableTimeslot(ConstraintFactory factory) {
return factory.forEachIncludingUnassigned(Talk.class)
.filter(talk -> talk.getTimeslot() != null)
.join(Speaker.class,
filtering((talk, speaker) -> talk.hasSpeaker(speaker)
&& speaker.getUnavailableTimeslots().contains(talk.getTimeslot())))
contain(Talk::getSpeakers, speaker -> speaker),
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lambda expression speaker -> speaker is an identity function. Depending on the API design, this could potentially be simplified to a method reference or omitted if the API supports it. Verify if the Joiners API provides a simpler alternative for identity mapping.

Copilot uses AI. Check for mistakes.
containedIn(Talk::getTimeslot, Speaker::getUnavailableTimeslots))
.penalize(HardSoftScore.ofHard(100), (talk, speaker) -> talk.getDurationInMinutes())
.justifyWith(
(talk, speaker, score) -> new UnavailableTimeslotJustification(talk, speaker))
Expand All @@ -173,7 +169,7 @@ Constraint talkPrerequisiteTalks(ConstraintFactory factory) {
return factory.forEach(Talk.class)
.join(Talk.class,
greaterThan(t -> t.getTimeslot().getEndDateTime(), t -> t.getTimeslot().getStartDateTime()),
filtering((talk1, talk2) -> talk2.getPrerequisiteTalks().contains(talk1)))
containedIn(talk -> talk, Talk::getPrerequisiteTalks))
.penalize(HardSoftScore.ofHard(10), Talk::combinedDurationInMinutes)
.justifyWith(
(talk, talk2, score) -> new ConferenceSchedulingJustification(
Expand All @@ -184,7 +180,7 @@ Constraint talkPrerequisiteTalks(ConstraintFactory factory) {
Constraint talkMutuallyExclusiveTalksTags(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
overlapping(t -> t.getTimeslot().getStartDateTime(), t -> t.getTimeslot().getEndDateTime()),
filtering((talk1, talk2) -> talk2.overlappingMutuallyExclusiveTalksTagCount(talk1) > 0))
containAny(Talk::getMutuallyExclusiveTalksTags))
.penalize(HardSoftScore.ofHard(1), (talk1, talk2) -> talk1.overlappingMutuallyExclusiveTalksTagCount(talk2) *
talk1.overlappingDurationInMinutes(talk2))
.justifyWith((talk, talk2, score) -> new ConflictTalkJustification("mutually-exclusive-talks tags", talk,
Expand Down Expand Up @@ -341,7 +337,7 @@ Constraint talkProhibitedRoomTags(ConstraintFactory factory) {
Constraint themeTrackConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
overlapping(t -> t.getTimeslot().getStartDateTime(), t -> t.getTimeslot().getEndDateTime()),
filtering((talk1, talk2) -> talk2.overlappingThemeTrackCount(talk1) > 0))
containAny(Talk::getThemeTrackTags))
.penalize(HardSoftScore.ofSoft(10), (talk1, talk2) -> talk1.overlappingThemeTrackCount(talk2) *
talk1.overlappingDurationInMinutes(talk2))
.justifyWith(
Expand All @@ -353,7 +349,7 @@ Constraint themeTrackConflict(ConstraintFactory factory) {
Constraint themeTrackRoomStability(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
equal(talk -> talk.getTimeslot().getStartDateTime().toLocalDate()),
filtering((talk1, talk2) -> talk2.overlappingThemeTrackCount(talk1) > 0))
containAny(Talk::getThemeTrackTags))
.filter((talk1, talk2) -> !talk1.getRoom().equals(talk2.getRoom()))
.penalize(HardSoftScore.ofSoft(10), (talk1, talk2) -> talk1.overlappingThemeTrackCount(talk2) *
talk1.combinedDurationInMinutes(talk2))
Expand All @@ -371,7 +367,7 @@ Constraint themeTrackRoomStability(ConstraintFactory factory) {
Constraint sectorConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
overlapping(t -> t.getTimeslot().getStartDateTime(), t -> t.getTimeslot().getEndDateTime()),
filtering((talk1, talk2) -> talk2.overlappingSectorCount(talk1) > 0))
containAny(Talk::getSectorTags))
.penalize(HardSoftScore.ofSoft(10), (talk1, talk2) -> talk1.overlappingSectorCount(talk2)
* talk1.overlappingDurationInMinutes(talk2))
.justifyWith((talk, talk2, score) -> new ConflictTalkJustification("sector", talk, talk.getSectorTags(), talk2,
Expand All @@ -382,7 +378,7 @@ Constraint sectorConflict(ConstraintFactory factory) {
Constraint audienceTypeDiversity(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
equal(Talk::getTimeslot),
filtering((talk1, talk2) -> talk2.overlappingAudienceTypeCount(talk1) > 0))
containAny(Talk::getAudienceTypes))
.reward(HardSoftScore.ofSoft(1), (talk1, talk2) -> talk1.overlappingAudienceTypeCount(talk2)
* talk1.getTimeslot().getDurationInMinutes())
.justifyWith((talk, talk2, score) -> new DiversityTalkJustification("audience types", talk,
Expand All @@ -393,8 +389,8 @@ Constraint audienceTypeDiversity(ConstraintFactory factory) {
Constraint audienceTypeThemeTrackConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
overlapping(t -> t.getTimeslot().getStartDateTime(), t -> t.getTimeslot().getEndDateTime()),
filtering((talk1, talk2) -> talk2.overlappingThemeTrackCount(talk1) > 0),
filtering((talk1, talk2) -> talk2.overlappingAudienceTypeCount(talk1) > 0))
containAny(Talk::getThemeTrackTags),
containAny(Talk::getAudienceTypes))
.penalize(HardSoftScore.ofSoft(1), (talk1, talk2) -> talk1.overlappingThemeTrackCount(talk2)
* talk1.overlappingAudienceTypeCount(talk2)
* talk1.overlappingDurationInMinutes(talk2))
Expand All @@ -420,7 +416,7 @@ Constraint contentAudienceLevelFlowViolation(ConstraintFactory factory) {
lessThan(Talk::getAudienceLevel),
greaterThan(talk1 -> talk1.getTimeslot().getEndDateTime(),
talk2 -> talk2.getTimeslot().getStartDateTime()),
filtering((talk1, talk2) -> talk2.overlappingContentCount(talk1) > 0))
containAny(Talk::getContentTags))
.penalize(HardSoftScore.ofSoft(10), (talk1, talk2) -> talk1.overlappingContentCount(talk2)
* talk1.combinedDurationInMinutes(talk2))
.justifyWith((talk, talk2, score) -> new ConferenceSchedulingJustification(
Expand All @@ -435,7 +431,7 @@ Constraint contentAudienceLevelFlowViolation(ConstraintFactory factory) {
Constraint contentConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
overlapping(t -> t.getTimeslot().getStartDateTime(), t -> t.getTimeslot().getEndDateTime()),
filtering((talk1, talk2) -> talk2.overlappingContentCount(talk1) > 0))
containAny(Talk::getContentTags))
.penalize(HardSoftScore.ofSoft(100), (talk1, talk2) -> talk1.overlappingContentCount(talk2)
* talk1.overlappingDurationInMinutes(talk2))
.justifyWith(
Expand Down
Loading