<commit_before>=== Lab 201: Tennis friends

==== Problem description

A number of couples play double tennis together. Each couple is a team.
They rent 2 tennis courts every Tuesday evening during 4 months (18 weeks). There are 7 teams.
On a given Tuesday evening, 4 distinct teams show up and they play 3 matches each: one against every other team there.

Decide which teams play on which Tuesday evening.

Not every Tuesday evening fits for every team: every team marks the dates which it is unable to play.
All teams like to play as much as possible, so distribution should be fair (as far as that's possible):
all teams should play an almost equal number of times.
Nice to have: They don't like to play against the same opponent every time,
so it would be nice to have that all each team plays against every other teams a similar number of times.

Presume that the number of teams (7) and the number of weeks (18) can change every season.

==== Assignment

. Draw an UML class diagram of the domain objects needed for solving the optimization problem. Include:
.. All relevant domain classes for solving the optimization problem. For example: `Team`, `Day`, ...
.. Every relationship between those classes, including cardinality. For example: `Foo` has a `ManyToOne` relationship to `Bar`.
... Do not include non-domain classes, such as `Solver` or `TennisDao` etc.
... Adding fields/properties/methods for each class is entirely optional.
.. Annotate which classes have an `@PlanningEntity` annotation.
.. Annotate which properties of each class have an `@PlanningVariable` annotation.
.. Annotate which classes have an `@PlanningSolution` annotation.
.. Annotate which fields/properties of each class have an `@PlanningEntityCollectionProperty` annotation.
. Calculate the size of the search space (without taking the unavailability constraint into account).
. How many constraint types (= how many score rules) do you expect to become implemented?

*Do not look at the Tennis example in the documentation or examples.* That's cheating.
The problem description above is intentionally less straightforward.

==== Success criteria

. A drawing of an UML class diagram which resembles the class diagram of the CloudBalancing example.
.. Add the `@PlanningEntity` annotation and the `@PlanningVariable` annotation where needed.
. Search space size
. Number of constraint types

==== Tips

* Ask yourself: what are the pigeons and what are the pigeons holes?
* It's highly recommended that the number of planning entities is stable during solving.
* Read up about how to draw an https://en.wikipedia.org/wiki/Class_diagram[UML class diagram].

[.solution]
==== Solution

===== Class design

image::lab201_tennisClassDiagram.png[]

* What are the pigeons and what are the pigeons holes?
** Every Tuesday, there are 4 spots to fill. Over 18 weeks that makes 72 spots to fill.
*** Each of those spots, is a `TeamAssignment` instance. That class is the planning entity.
*** *A `TeamAssignment` is clearly defined* by a `Day` and an `indexInDay` (0, 1, 2 or 3). Those 2 properties are a unique business key, immutable during solving.
*** OptaPlanner's job is to find the best `Team` to schedule for each spot, so `TeamAssignment.getTeam()` is the planning variable (so definitely not immutable during solving).
*** Notice how `TeamAssignment` has a `ManyToOne` relationship to `Team`.
* Why is `TeamAssignment.getDay()` _not_ a planning variable?
** Despite that `TeamAssignment` has a `ManyToOne` relationship to `Day`, it makes a poor planning variable, because `Team` makes a poor business key (part).
** Even if the day is a planning variable, a `TeamAssignment` cannot fill in the `Team` field decidedly:
*** The number of times each team plays is unknown.
**** Some teams will play 10 days, others 11 days (because `18*4/7 = 10.29`), presuming the unavailability constraint allow this.
**** If the unavailability constraint is very disruptive, some teams might even only play 9 days or less, possibly even allowing others to play 12 days or more.
* Why not make both `TeamAssignment.getDay()` and `TeamAssignment.getTeam()` each a planning variable?
** Then what defines a `TeamAssignment`?
** This would blow up the search space a lot more. This impacts Solver speed a lot.
** The build-in fine grained moves might no longer suffice to get efficiently from feasible solution to feasible solution. This impacts Solver speed.

===== Search space size

`valueSize^entitySize^ = 7^72^ = 10^60^`

===== Number of constraint types

4 constraint types (score rules):

* oneAssignmentPerDatePerTeam (hard)
* unavailabilityPenalty (hard)
* fairAssignmentCountPerTeam (medium)
* evenlyConfrontationCount (soft)
<commit_msg>Spelling: replace build-in with built-in<commit_after>=== Lab 201: Tennis friends

==== Problem description

A number of couples play double tennis together. Each couple is a team.
They rent 2 tennis courts every Tuesday evening during 4 months (18 weeks). There are 7 teams.
On a given Tuesday evening, 4 distinct teams show up and they play 3 matches each: one against every other team there.

Decide which teams play on which Tuesday evening.

Not every Tuesday evening fits for every team: every team marks the dates which it is unable to play.
All teams like to play as much as possible, so distribution should be fair (as far as that's possible):
all teams should play an almost equal number of times.
Nice to have: They don't like to play against the same opponent every time,
so it would be nice to have that all each team plays against every other teams a similar number of times.

Presume that the number of teams (7) and the number of weeks (18) can change every season.

==== Assignment

. Draw an UML class diagram of the domain objects needed for solving the optimization problem. Include:
.. All relevant domain classes for solving the optimization problem. For example: `Team`, `Day`, ...
.. Every relationship between those classes, including cardinality. For example: `Foo` has a `ManyToOne` relationship to `Bar`.
... Do not include non-domain classes, such as `Solver` or `TennisDao` etc.
... Adding fields/properties/methods for each class is entirely optional.
.. Annotate which classes have an `@PlanningEntity` annotation.
.. Annotate which properties of each class have an `@PlanningVariable` annotation.
.. Annotate which classes have an `@PlanningSolution` annotation.
.. Annotate which fields/properties of each class have an `@PlanningEntityCollectionProperty` annotation.
. Calculate the size of the search space (without taking the unavailability constraint into account).
. How many constraint types (= how many score rules) do you expect to become implemented?

*Do not look at the Tennis example in the documentation or examples.* That's cheating.
The problem description above is intentionally less straightforward.

==== Success criteria

. A drawing of an UML class diagram which resembles the class diagram of the CloudBalancing example.
.. Add the `@PlanningEntity` annotation and the `@PlanningVariable` annotation where needed.
. Search space size
. Number of constraint types

==== Tips

* Ask yourself: what are the pigeons and what are the pigeons holes?
* It's highly recommended that the number of planning entities is stable during solving.
* Read up about how to draw an https://en.wikipedia.org/wiki/Class_diagram[UML class diagram].

[.solution]
==== Solution

===== Class design

image::lab201_tennisClassDiagram.png[]

* What are the pigeons and what are the pigeons holes?
** Every Tuesday, there are 4 spots to fill. Over 18 weeks that makes 72 spots to fill.
*** Each of those spots, is a `TeamAssignment` instance. That class is the planning entity.
*** *A `TeamAssignment` is clearly defined* by a `Day` and an `indexInDay` (0, 1, 2 or 3). Those 2 properties are a unique business key, immutable during solving.
*** OptaPlanner's job is to find the best `Team` to schedule for each spot, so `TeamAssignment.getTeam()` is the planning variable (so definitely not immutable during solving).
*** Notice how `TeamAssignment` has a `ManyToOne` relationship to `Team`.
* Why is `TeamAssignment.getDay()` _not_ a planning variable?
** Despite that `TeamAssignment` has a `ManyToOne` relationship to `Day`, it makes a poor planning variable, because `Team` makes a poor business key (part).
** Even if the day is a planning variable, a `TeamAssignment` cannot fill in the `Team` field decidedly:
*** The number of times each team plays is unknown.
**** Some teams will play 10 days, others 11 days (because `18*4/7 = 10.29`), presuming the unavailability constraint allow this.
**** If the unavailability constraint is very disruptive, some teams might even only play 9 days or less, possibly even allowing others to play 12 days or more.
* Why not make both `TeamAssignment.getDay()` and `TeamAssignment.getTeam()` each a planning variable?
** Then what defines a `TeamAssignment`?
** This would blow up the search space a lot more. This impacts Solver speed a lot.
** The built-in fine grained moves might no longer suffice to get efficiently from feasible solution to feasible solution. This impacts Solver speed.

===== Search space size

`valueSize^entitySize^ = 7^72^ = 10^60^`

===== Number of constraint types

4 constraint types (score rules):

* oneAssignmentPerDatePerTeam (hard)
* unavailabilityPenalty (hard)
* fairAssignmentCountPerTeam (medium)
* evenlyConfrontationCount (soft)

{'fraction_non_alphanumeric': 0.07558938577016598, 'fraction_numerical': 0.01036050322444233, 'mean_word_length': 4.634306134603931, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6612695', 'n_tokens_mistral': 2479, 'n_tokens_neox': 2344, 'n_words': 1350}