Skip to content

Commit

Permalink
feat: Add missing of methods to scores in Python
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher-Chianelli committed Aug 26, 2024
1 parent 5352fde commit 9a410ca
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 21 deletions.
138 changes: 126 additions & 12 deletions python/python-core/src/main/python/score/_score.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from abc import ABC, abstractmethod
from typing import ClassVar
from dataclasses import dataclass, field
from jpype import JArray, JLong
from decimal import Decimal
from jpype import JArray, JLong
from typing import ClassVar

from .._timefold_java_interop import _java_score_mapping_dict


Expand Down Expand Up @@ -75,6 +76,10 @@ def is_feasible(self) -> bool:
def of(score: int) -> 'SimpleScore':
return SimpleScore(score, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, score: int) -> 'SimpleScore':
return SimpleScore(score, init_score=init_score)

@staticmethod
def parse(score_text: str) -> 'SimpleScore':
if 'init' in score_text:
Expand Down Expand Up @@ -138,6 +143,18 @@ def is_feasible(self) -> bool:
def of(hard_score: int, soft_score: int) -> 'HardSoftScore':
return HardSoftScore(hard_score, soft_score, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, hard_score: int, soft_score: int) -> 'HardSoftScore':
return HardSoftScore(hard_score, soft_score, init_score=init_score)

@staticmethod
def of_hard(hard_score: int) -> 'HardSoftScore':
return HardSoftScore(hard_score, 0, init_score=0)

@staticmethod
def of_soft(soft_score: int) -> 'HardSoftScore':
return HardSoftScore(0, soft_score, init_score=0)

@staticmethod
def parse(score_text: str) -> 'HardSoftScore':
if 'init' in score_text:
Expand All @@ -161,8 +178,8 @@ def __str__(self):


HardSoftScore.ZERO = HardSoftScore.of(0, 0)
HardSoftScore.ONE_HARD = HardSoftScore.of(1, 0)
HardSoftScore.ONE_SOFT = HardSoftScore.of(0, 1)
HardSoftScore.ONE_HARD = HardSoftScore.of_hard(1)
HardSoftScore.ONE_SOFT = HardSoftScore.of_soft(1)


@dataclass(unsafe_hash=True, order=True)
Expand Down Expand Up @@ -215,6 +232,22 @@ def is_feasible(self) -> bool:
def of(hard_score: int, medium_score: int, soft_score: int) -> 'HardMediumSoftScore':
return HardMediumSoftScore(hard_score, medium_score, soft_score, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, hard_score: int, medium_score: int, soft_score: int) -> 'HardMediumSoftScore':
return HardMediumSoftScore(hard_score, medium_score, soft_score, init_score=init_score)

@staticmethod
def of_hard(hard_score: int) -> 'HardMediumSoftScore':
return HardMediumSoftScore(hard_score, 0, 0, init_score=0)

@staticmethod
def of_medium(medium_score: int) -> 'HardMediumSoftScore':
return HardMediumSoftScore(0, medium_score, 0, init_score=0)

@staticmethod
def of_soft(soft_score: int) -> 'HardMediumSoftScore':
return HardMediumSoftScore(0, 0, soft_score, init_score=0)

@staticmethod
def parse(score_text: str) -> 'HardMediumSoftScore':
if 'init' in score_text:
Expand All @@ -240,9 +273,9 @@ def __str__(self):


HardMediumSoftScore.ZERO = HardMediumSoftScore.of(0, 0, 0)
HardMediumSoftScore.ONE_HARD = HardMediumSoftScore.of(1, 0, 0)
HardMediumSoftScore.ONE_MEDIUM = HardMediumSoftScore.of(0, 1, 0)
HardMediumSoftScore.ONE_SOFT = HardMediumSoftScore.of(0, 0, 1)
HardMediumSoftScore.ONE_HARD = HardMediumSoftScore.of_hard(1)
HardMediumSoftScore.ONE_MEDIUM = HardMediumSoftScore.of_medium(1)
HardMediumSoftScore.ONE_SOFT = HardMediumSoftScore.of_soft(1)


@dataclass(unsafe_hash=True, order=True)
Expand All @@ -268,10 +301,33 @@ class BendableScore(Score):
def is_feasible(self) -> bool:
return self.is_solution_initialized and all(score >= 0 for score in self.hard_scores)

@staticmethod
def zero(hard_levels_size: int, soft_levels_size: int) -> 'BendableScore':
return BendableScore(tuple([0] * hard_levels_size), tuple([0] * soft_levels_size))

@staticmethod
def of(hard_scores: tuple[int, ...], soft_scores: tuple[int, ...]) -> 'BendableScore':
return BendableScore(hard_scores, soft_scores, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, hard_scores: tuple[int, ...],
soft_scores: tuple[int, ...]) -> 'BendableScore':
return BendableScore(hard_scores, soft_scores, init_score=init_score)

@staticmethod
def of_hard(hard_levels_size: int, soft_levels_size: int, hard_level: int, hard_score: int) -> 'BendableScore':
hard_scores = [0] * hard_levels_size
hard_scores[hard_level] = hard_score
soft_scores = [0] * soft_levels_size
return BendableScore(tuple(hard_scores), tuple(soft_scores), init_score=0)

@staticmethod
def of_soft(hard_levels_size: int, soft_levels_size: int, soft_level: int, soft_score: int) -> 'BendableScore':
hard_scores = [0] * hard_levels_size
soft_scores = [0] * soft_levels_size
soft_scores[soft_level] = soft_score
return BendableScore(tuple(hard_scores), tuple(soft_scores), init_score=0)

@staticmethod
def parse(score_text: str) -> 'BendableScore':
if 'init' in score_text:
Expand Down Expand Up @@ -333,6 +389,10 @@ def is_feasible(self) -> bool:
def of(score: Decimal) -> 'SimpleDecimalScore':
return SimpleDecimalScore(score, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, score: Decimal) -> 'SimpleDecimalScore':
return SimpleDecimalScore(score, init_score=init_score)

@staticmethod
def parse(score_text: str) -> 'SimpleDecimalScore':
if 'init' in score_text:
Expand Down Expand Up @@ -396,6 +456,18 @@ def is_feasible(self) -> bool:
def of(hard_score: Decimal, soft_score: Decimal) -> 'HardSoftDecimalScore':
return HardSoftDecimalScore(hard_score, soft_score, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, hard_score: Decimal, soft_score: Decimal) -> 'HardSoftDecimalScore':
return HardSoftDecimalScore(hard_score, soft_score, init_score=init_score)

@staticmethod
def of_hard(hard_score: Decimal) -> 'HardSoftDecimalScore':
return HardSoftDecimalScore(hard_score, Decimal(0), init_score=0)

@staticmethod
def of_soft(soft_score: Decimal) -> 'HardSoftDecimalScore':
return HardSoftDecimalScore(Decimal(0), soft_score, init_score=0)

@staticmethod
def parse(score_text: str) -> 'HardSoftDecimalScore':
if 'init' in score_text:
Expand All @@ -419,8 +491,8 @@ def __str__(self):


HardSoftDecimalScore.ZERO = HardSoftDecimalScore.of(Decimal(0), Decimal(0))
HardSoftDecimalScore.ONE_HARD = HardSoftDecimalScore.of(Decimal(1), Decimal(0))
HardSoftDecimalScore.ONE_SOFT = HardSoftDecimalScore.of(Decimal(0), Decimal(1))
HardSoftDecimalScore.ONE_HARD = HardSoftDecimalScore.of_hard(Decimal(1))
HardSoftDecimalScore.ONE_SOFT = HardSoftDecimalScore.of_soft(Decimal(1))


@dataclass(unsafe_hash=True, order=True)
Expand Down Expand Up @@ -473,6 +545,23 @@ def is_feasible(self) -> bool:
def of(hard_score: Decimal, medium_score: Decimal, soft_score: Decimal) -> 'HardMediumSoftDecimalScore':
return HardMediumSoftDecimalScore(hard_score, medium_score, soft_score, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, hard_score: Decimal, medium_score: Decimal,
soft_score: Decimal) -> 'HardMediumSoftDecimalScore':
return HardMediumSoftDecimalScore(hard_score, medium_score, soft_score, init_score=init_score)

@staticmethod
def of_hard(hard_score: Decimal) -> 'HardMediumSoftDecimalScore':
return HardMediumSoftDecimalScore(hard_score, Decimal(0), Decimal(0), init_score=0)

@staticmethod
def of_medium(medium_score: Decimal) -> 'HardMediumSoftDecimalScore':
return HardMediumSoftDecimalScore(Decimal(0), medium_score, Decimal(0), init_score=0)

@staticmethod
def of_soft(soft_score: Decimal) -> 'HardMediumSoftDecimalScore':
return HardMediumSoftDecimalScore(Decimal(0), Decimal(0), soft_score, init_score=0)

@staticmethod
def parse(score_text: str) -> 'HardMediumSoftDecimalScore':
if 'init' in score_text:
Expand All @@ -498,9 +587,9 @@ def __str__(self):


HardMediumSoftDecimalScore.ZERO = HardMediumSoftDecimalScore.of(Decimal(0), Decimal(0), Decimal(0))
HardMediumSoftDecimalScore.ONE_HARD = HardMediumSoftDecimalScore.of(Decimal(1), Decimal(0), Decimal(0))
HardMediumSoftDecimalScore.ONE_MEDIUM = HardMediumSoftDecimalScore.of(Decimal(0), Decimal(1), Decimal(0))
HardMediumSoftDecimalScore.ONE_SOFT = HardMediumSoftDecimalScore.of(Decimal(0), Decimal(0), Decimal(1))
HardMediumSoftDecimalScore.ONE_HARD = HardMediumSoftDecimalScore.of_hard(Decimal(1))
HardMediumSoftDecimalScore.ONE_MEDIUM = HardMediumSoftDecimalScore.of_medium(Decimal(1))
HardMediumSoftDecimalScore.ONE_SOFT = HardMediumSoftDecimalScore.of_soft(Decimal(1))


@dataclass(unsafe_hash=True, order=True)
Expand All @@ -526,10 +615,35 @@ class BendableDecimalScore(Score):
def is_feasible(self) -> bool:
return self.is_solution_initialized and all(score >= 0 for score in self.hard_scores)

@staticmethod
def zero(hard_levels_size: int, soft_levels_size: int) -> 'BendableDecimalScore':
return BendableDecimalScore(tuple([Decimal(0)] * hard_levels_size), tuple([Decimal(0)] * soft_levels_size))

@staticmethod
def of(hard_scores: tuple[Decimal, ...], soft_scores: tuple[Decimal, ...]) -> 'BendableDecimalScore':
return BendableDecimalScore(hard_scores, soft_scores, init_score=0)

@staticmethod
def of_uninitialized(init_score: int, hard_scores: tuple[Decimal, ...], soft_scores: tuple[Decimal, ...]) -> \
'BendableDecimalScore':
return BendableDecimalScore(hard_scores, soft_scores, init_score=init_score)

@staticmethod
def of_hard(hard_levels_size: int, soft_levels_size: int, hard_level: int, hard_score: Decimal) -> \
'BendableDecimalScore':
hard_scores = [Decimal(0)] * hard_levels_size
hard_scores[hard_level] = hard_score
soft_scores = [Decimal(0)] * soft_levels_size
return BendableDecimalScore(tuple(hard_scores), tuple(soft_scores), init_score=0)

@staticmethod
def of_soft(hard_levels_size: int, soft_levels_size: int, soft_level: int, soft_score: Decimal) -> \
'BendableDecimalScore':
hard_scores = [Decimal(0)] * hard_levels_size
soft_scores = [Decimal(0)] * soft_levels_size
soft_scores[soft_level] = soft_score
return BendableDecimalScore(tuple(hard_scores), tuple(soft_scores), init_score=0)

@staticmethod
def parse(score_text: str) -> 'BendableDecimalScore':
if 'init' in score_text:
Expand Down
52 changes: 43 additions & 9 deletions python/python-core/tests/test_score.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Annotated

def test_simple_score():
uninit_score = SimpleScore(10, init_score=-2)
uninit_score = SimpleScore.of_uninitialized(-2, 10)
score = SimpleScore.of(10)

assert str(uninit_score) == '-2init/10'
Expand All @@ -18,40 +18,57 @@ def test_simple_score():


def test_hard_soft_score():
uninit_score = HardSoftScore(100, 20, init_score=-3)
uninit_score = HardSoftScore.of_uninitialized(-3, 100, 20)
score = HardSoftScore.of(100, 20)
soft_score = HardSoftScore.of_soft(3)
hard_score = HardSoftScore.of_hard(3)

assert str(uninit_score) == '-3init/100hard/20soft'
assert str(score) == '100hard/20soft'

assert HardSoftScore.parse('-3init/100hard/20soft') == uninit_score
assert HardSoftScore.parse('100hard/20soft') == score

assert soft_score == HardSoftScore(0, 3)
assert hard_score == HardSoftScore(3, 0)


def test_hard_medium_soft_score():
uninit_score = HardMediumSoftScore(1000, 200, 30, init_score=-4)
uninit_score = HardMediumSoftScore.of_uninitialized(-4, 1000, 200, 30)
score = HardMediumSoftScore.of(1000, 200, 30)
soft_score = HardMediumSoftScore.of_soft(3)
medium_score = HardMediumSoftScore.of_medium(3)
hard_score = HardMediumSoftScore.of_hard(3)

assert str(uninit_score) == '-4init/1000hard/200medium/30soft'
assert str(score) == '1000hard/200medium/30soft'

assert HardMediumSoftScore.parse('-4init/1000hard/200medium/30soft') == uninit_score
assert HardMediumSoftScore.parse('1000hard/200medium/30soft') == score

assert soft_score == HardMediumSoftScore(0, 0, 3)
assert medium_score == HardMediumSoftScore(0, 3, 0)
assert hard_score == HardMediumSoftScore(3, 0, 0)


def test_bendable_score():
uninit_score = BendableScore((1, -2, 3), (-30, 40), init_score=-500)
uninit_score = BendableScore.of_uninitialized(-500, (1, -2, 3), (-30, 40))
score = BendableScore.of((1, -2, 3), (-30, 40))
soft_score = BendableScore.of_soft(3, 2, 1, 5)
hard_score = BendableScore.of_hard(3, 2, 1, 5)

assert str(uninit_score) == '-500init/[1/-2/3]hard/[-30/40]soft'
assert str(score) == '[1/-2/3]hard/[-30/40]soft'

assert BendableScore.parse('-500init/[1/-2/3]hard/[-30/40]soft') == uninit_score
assert BendableScore.parse('[1/-2/3]hard/[-30/40]soft') == score

assert soft_score == BendableScore((0, 0, 0), (0, 5))
assert hard_score == BendableScore((0, 5, 0), (0, 0))


def test_simple_decimal_score():
uninit_score = SimpleDecimalScore(Decimal('10.1'), init_score=-2)
uninit_score = SimpleDecimalScore.of_uninitialized(-2, Decimal('10.1'))
score = SimpleDecimalScore.of(Decimal('10.1'))

assert str(uninit_score) == '-2init/10.1'
Expand All @@ -62,39 +79,56 @@ def test_simple_decimal_score():


def test_hard_soft_decimal_score():
uninit_score = HardSoftDecimalScore(Decimal('100.1'), Decimal('20.2'), init_score=-3)
uninit_score = HardSoftDecimalScore.of_uninitialized(-3, Decimal('100.1'), Decimal('20.2'))
score = HardSoftDecimalScore.of(Decimal('100.1'), Decimal('20.2'))
soft_score = HardSoftDecimalScore.of_soft(Decimal(3))
hard_score = HardSoftDecimalScore.of_hard(Decimal(3))

assert str(uninit_score) == '-3init/100.1hard/20.2soft'
assert str(score) == '100.1hard/20.2soft'

assert HardSoftDecimalScore.parse('-3init/100.1hard/20.2soft') == uninit_score
assert HardSoftDecimalScore.parse('100.1hard/20.2soft') == score

assert soft_score == HardSoftDecimalScore(Decimal(0), Decimal(3))
assert hard_score == HardSoftDecimalScore(Decimal(3), Decimal(0))


def test_hard_medium_soft_decimal_score():
uninit_score = HardMediumSoftDecimalScore(Decimal('1000.1'), Decimal('200.2'), Decimal('30.3'), init_score=-4)
uninit_score = HardMediumSoftDecimalScore.of_uninitialized(-4, Decimal('1000.1'), Decimal('200.2'), Decimal('30.3'))
score = HardMediumSoftDecimalScore.of(Decimal('1000.1'), Decimal('200.2'), Decimal('30.3'))
soft_score = HardMediumSoftDecimalScore.of_soft(Decimal(3))
medium_score = HardMediumSoftDecimalScore.of_medium(Decimal(3))
hard_score = HardMediumSoftDecimalScore.of_hard(Decimal(3))

assert str(uninit_score) == '-4init/1000.1hard/200.2medium/30.3soft'
assert str(score) == '1000.1hard/200.2medium/30.3soft'

assert HardMediumSoftDecimalScore.parse('-4init/1000.1hard/200.2medium/30.3soft') == uninit_score
assert HardMediumSoftDecimalScore.parse('1000.1hard/200.2medium/30.3soft') == score

assert soft_score == HardMediumSoftDecimalScore(Decimal(0), Decimal(0), Decimal(3))
assert medium_score == HardMediumSoftDecimalScore(Decimal(0), Decimal(3), Decimal(0))
assert hard_score == HardMediumSoftDecimalScore(Decimal(3), Decimal(0), Decimal(0))


def test_bendable_decimal_score():
uninit_score = BendableDecimalScore((Decimal('1.1'), Decimal('-2.2'), Decimal('3.3')),
(Decimal('-30.3'), Decimal('40.4')), init_score=-500)
uninit_score = BendableDecimalScore.of_uninitialized(-500, (Decimal('1.1'), Decimal('-2.2'), Decimal('3.3')),
(Decimal('-30.3'), Decimal('40.4')))
score = BendableDecimalScore.of((Decimal('1.1'), Decimal('-2.2'), Decimal('3.3')),
(Decimal('-30.3'), Decimal('40.4')))
soft_score = BendableDecimalScore.of_soft(3, 2, 1, Decimal(5))
hard_score = BendableDecimalScore.of_hard(3, 2, 1, Decimal(5))

assert str(uninit_score) == '-500init/[1.1/-2.2/3.3]hard/[-30.3/40.4]soft'
assert str(score) == '[1.1/-2.2/3.3]hard/[-30.3/40.4]soft'

assert BendableDecimalScore.parse('-500init/[1.1/-2.2/3.3]hard/[-30.3/40.4]soft') == uninit_score
assert BendableDecimalScore.parse('[1.1/-2.2/3.3]hard/[-30.3/40.4]soft') == score

assert soft_score == BendableDecimalScore((Decimal(0), Decimal(0), Decimal(0)), (Decimal(0), Decimal(5)))
assert hard_score == BendableDecimalScore((Decimal(0), Decimal(5), Decimal(0)), (Decimal(0), Decimal(0)))


def test_sanity_score_type():
@planning_entity
Expand Down

0 comments on commit 9a410ca

Please sign in to comment.