@@ -396,4 +396,76 @@ public void mapPlanningEntityChanges() {
396396 assertMatch (entity2 ));
397397 }
398398
399+ /**
400+ * @see <a href="https://github.com/TimefoldAI/timefold-solver/issues/828">Timefold Solver Github Issue 828</a>
401+ */
402+ @ TestTemplate
403+ public void concatSameTupleDeadAndAlive () {
404+ InnerScoreDirector <TestdataSolution , SimpleScore > scoreDirector =
405+ buildScoreDirector (TestdataSolution .buildSolutionDescriptor (),
406+ factory -> new Constraint [] {
407+ factory .forEach (TestdataEntity .class )
408+ .filter (e -> e .getValue ().getCode ().equals ("A" ))
409+ .concat (factory .forEach (TestdataEntity .class ))
410+ .penalize (SimpleScore .ONE )
411+ .asConstraint (TEST_CONSTRAINT_NAME )
412+ });
413+
414+ TestdataSolution solution = TestdataSolution .generateSolution (2 , 2 );
415+ TestdataEntity entity1 = solution .getEntityList ().get (0 );
416+ TestdataEntity entity2 = solution .getEntityList ().get (1 );
417+ TestdataValue valueA = solution .getValueList ().get (0 );
418+ valueA .setCode ("A" );
419+ TestdataValue valueB = solution .getValueList ().get (1 );
420+ valueB .setCode ("B" );
421+
422+ scoreDirector .setWorkingSolution (solution );
423+ assertScore (scoreDirector ,
424+ assertMatch (entity1 ),
425+ assertMatch (entity1 ),
426+ assertMatch (entity2 ));
427+
428+ scoreDirector .beforeVariableChanged (entity1 , "value" );
429+ entity1 .setValue (valueB );
430+ scoreDirector .afterVariableChanged (entity1 , "value" );
431+ scoreDirector .beforeVariableChanged (entity2 , "value" );
432+ entity2 .setValue (valueA );
433+ scoreDirector .afterVariableChanged (entity2 , "value" );
434+ assertScore (scoreDirector ,
435+ assertMatch (entity1 ),
436+ assertMatch (entity2 ),
437+ assertMatch (entity2 ));
438+
439+ scoreDirector .beforeVariableChanged (entity1 , "value" );
440+ entity1 .setValue (valueA );
441+ scoreDirector .afterVariableChanged (entity1 , "value" );
442+ scoreDirector .beforeVariableChanged (entity2 , "value" );
443+ entity2 .setValue (valueB );
444+ scoreDirector .afterVariableChanged (entity2 , "value" );
445+ // Do not recalculate score, since this is undo
446+
447+ scoreDirector .beforeVariableChanged (entity2 , "value" );
448+ entity2 .setValue (valueA );
449+ scoreDirector .afterVariableChanged (entity2 , "value" );
450+ scoreDirector .beforeVariableChanged (entity1 , "value" );
451+ entity1 .setValue (valueB );
452+ scoreDirector .afterVariableChanged (entity1 , "value" );
453+ assertScore (scoreDirector ,
454+ assertMatch (entity1 ),
455+ assertMatch (entity2 ),
456+ assertMatch (entity2 ));
457+
458+ scoreDirector .beforeVariableChanged (entity2 , "value" );
459+ entity2 .setValue (valueB );
460+ scoreDirector .afterVariableChanged (entity2 , "value" );
461+ scoreDirector .beforeVariableChanged (entity1 , "value" );
462+ entity1 .setValue (valueA );
463+ scoreDirector .afterVariableChanged (entity1 , "value" );
464+
465+ assertScore (scoreDirector ,
466+ assertMatch (entity1 ),
467+ assertMatch (entity1 ),
468+ assertMatch (entity2 ));
469+ }
470+
399471}
0 commit comments