Skip to content

Commit 3942327

Browse files
feat: Add parse methods to all score implementations (#77)
1 parent 025f835 commit 3942327

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed

tests/test_score.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from timefold.solver.score import SimpleScore, HardSoftScore, HardMediumSoftScore, BendableScore
2+
3+
4+
def test_simple_score():
5+
uninit_score = SimpleScore(10, init_score=-2)
6+
score = SimpleScore.of(10)
7+
8+
assert str(uninit_score) == '-2init/10'
9+
assert str(score) == '10'
10+
11+
assert SimpleScore.parse('-2init/10') == uninit_score
12+
assert SimpleScore.parse('10') == score
13+
14+
15+
def test_hard_soft_score():
16+
uninit_score = HardSoftScore(100, 20, init_score=-3)
17+
score = HardSoftScore.of(100, 20)
18+
19+
assert str(uninit_score) == '-3init/100hard/20soft'
20+
assert str(score) == '100hard/20soft'
21+
22+
assert HardSoftScore.parse('-3init/100hard/20soft') == uninit_score
23+
assert HardSoftScore.parse('100hard/20soft') == score
24+
25+
26+
def test_hard_medium_soft_score():
27+
uninit_score = HardMediumSoftScore(1000, 200, 30, init_score=-4)
28+
score = HardMediumSoftScore.of(1000, 200, 30)
29+
30+
assert str(uninit_score) == '-4init/1000hard/200medium/30soft'
31+
assert str(score) == '1000hard/200medium/30soft'
32+
33+
assert HardMediumSoftScore.parse('-4init/1000hard/200medium/30soft') == uninit_score
34+
assert HardMediumSoftScore.parse('1000hard/200medium/30soft') == score
35+
36+
37+
def test_bendable_score():
38+
uninit_score = BendableScore((1, -2, 3), (-30, 40), init_score=-500)
39+
score = BendableScore.of((1, -2, 3), (-30, 40))
40+
41+
assert str(uninit_score) == '-500init/[1/-2/3]hard/[-30/40]soft'
42+
assert str(score) == '[1/-2/3]hard/[-30/40]soft'
43+
44+
assert BendableScore.parse('-500init/[1/-2/3]hard/[-30/40]soft') == uninit_score
45+
assert BendableScore.parse('[1/-2/3]hard/[-30/40]soft') == score

timefold-solver-python-core/src/main/python/score/_score.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@ def is_feasible(self) -> bool:
7474
def of(score: int) -> 'SimpleScore':
7575
return SimpleScore(score, init_score=0)
7676

77+
@staticmethod
78+
def parse(score_text: str) -> 'SimpleScore':
79+
if 'init' in score_text:
80+
init, score = score_text.split('/')
81+
else:
82+
init = '0init'
83+
score = score_text
84+
85+
return SimpleScore(int(score), init_score=int(init.rstrip('init')))
86+
7787
def _to_java_score(self):
7888
if self.init_score < 0:
7989
return _java_score_mapping_dict['SimpleScore'].ofUninitialized(self.init_score, self.score)
@@ -127,6 +137,17 @@ def is_feasible(self) -> bool:
127137
def of(hard_score: int, soft_score: int) -> 'HardSoftScore':
128138
return HardSoftScore(hard_score, soft_score, init_score=0)
129139

140+
@staticmethod
141+
def parse(score_text: str) -> 'HardSoftScore':
142+
if 'init' in score_text:
143+
init, hard, soft = score_text.split('/')
144+
else:
145+
init = '0init'
146+
hard, soft = score_text.split('/')
147+
148+
return HardSoftScore(int(hard.rstrip('hard')), int(soft.rstrip('soft')),
149+
init_score=int(init.rstrip('init')))
150+
130151
def _to_java_score(self):
131152
if self.init_score < 0:
132153
return _java_score_mapping_dict['HardSoftScore'].ofUninitialized(self.init_score, self.hard_score, self.soft_score)
@@ -193,6 +214,17 @@ def is_feasible(self) -> bool:
193214
def of(hard_score: int, medium_score: int, soft_score: int) -> 'HardMediumSoftScore':
194215
return HardMediumSoftScore(hard_score, medium_score, soft_score, init_score=0)
195216

217+
@staticmethod
218+
def parse(score_text: str) -> 'HardMediumSoftScore':
219+
if 'init' in score_text:
220+
init, hard, medium, soft = score_text.split('/')
221+
else:
222+
init = '0init'
223+
hard, medium, soft = score_text.split('/')
224+
225+
return HardMediumSoftScore(int(hard.rstrip('hard')), int(medium.rstrip('medium')),
226+
int(soft.rstrip('soft')), init_score=int(init.rstrip('init')))
227+
196228
def _to_java_score(self):
197229
if self.init_score < 0:
198230
return _java_score_mapping_dict['HardMediumSoftScore'].ofUninitialized(self.init_score, self.hard_score,
@@ -239,6 +271,22 @@ def is_feasible(self) -> bool:
239271
def of(hard_scores: tuple[int, ...], soft_scores: tuple[int, ...]) -> 'BendableScore':
240272
return BendableScore(hard_scores, soft_scores, init_score=0)
241273

274+
@staticmethod
275+
def parse(score_text: str) -> 'BendableScore':
276+
if 'init' in score_text:
277+
init, hard_score_text, soft_score_text = score_text.split('/[')
278+
else:
279+
hard_score_text, soft_score_text = score_text.split('/[')
280+
# Remove leading [ from hard score text,
281+
# since there is no init score in the text
282+
# (and thus the split will not consume it)
283+
hard_score_text = hard_score_text[1:]
284+
init = '0init'
285+
286+
hard_scores = tuple([int(score) for score in hard_score_text[:hard_score_text.index(']')].split('/')])
287+
soft_scores = tuple([int(score) for score in soft_score_text[:soft_score_text.index(']')].split('/')])
288+
return BendableScore(hard_scores, soft_scores, init_score=int(init.rstrip('init')))
289+
242290
def _to_java_score(self):
243291
IntArrayCls = JArray(JInt)
244292
hard_scores = IntArrayCls(self.hard_scores)
@@ -249,8 +297,10 @@ def _to_java_score(self):
249297
return _java_score_mapping_dict['BendableScore'].of(hard_scores, soft_scores)
250298

251299
def __str__(self):
252-
return (f'{list(self.hard_scores)}hard/{list(self.soft_scores)}soft' if self.is_solution_initialized else
253-
f'{self.init_score}init/{list(self.hard_scores)}hard/{list(self.soft_scores)}soft')
300+
hard_text = f'{str(list(self.hard_scores)).replace(", ", "/")}hard'
301+
soft_text = f'{str(list(self.soft_scores)).replace(", ", "/")}soft'
302+
return (f'{hard_text}/{soft_text}' if self.is_solution_initialized else
303+
f'{self.init_score}init/{hard_text}/{soft_text}')
254304

255305

256306
# Import score conversions here to register conversions (circular import)

0 commit comments

Comments
 (0)