-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNumberMatch.py
More file actions
75 lines (62 loc) · 2.85 KB
/
NumberMatch.py
File metadata and controls
75 lines (62 loc) · 2.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from dataclasses import dataclass, field
from typing import List, Iterable
@dataclass
class NumberMatch:
"""
Represents a set of guessed numbers and two result counters:
- numbers: the list of guessed numbers (ints)
- numCorrect: how many guessed values appear anywhere in the secret
- numPositionCorrect: how many guessed values are correct and in the correct position
"""
number: int
numCorrect: int = 0
numPositionCorrect: int = 0
numbers: List[int] = field(default_factory=list)
def __post_init__(self):
# ensure numbers is a list of ints
self.numbers = list(map(int, str(self.number)))
# if isinstance(self.numbers, Iterable) and not isinstance(self.numbers, (str, bytes)):
# self.numbers = [int(x) for x in self.numbers]
# else:
# raise TypeError("numbers must be an iterable of integers")
# coerce counters to ints and basic validation
self.numCorrect = int(self.numCorrect)
self.numPositionCorrect = int(self.numPositionCorrect)
if self.numCorrect < 0 or self.numPositionCorrect < 0:
raise ValueError("Counters must be non-negative")
@classmethod
def from_string(cls, s: str, sep: str = ","):
"""
Create NumberMatch from a string like "1,2,3".
"""
parts = [p.strip() for p in s.split(sep) if p.strip() != ""]
nums = [int(p) for p in parts]
return cls(numbers=nums)
def update_counters(self, secret: Iterable[int]):
"""
Given a secret sequence of integers, compute:
- numPositionCorrect: count of elements equal in same index
- numCorrect: count of values from guess that appear in secret (multiset aware)
Both counters are updated on the instance and also returned as a tuple.
"""
secret_list = list(secret)
guess = self.numbers
# numPositionCorrect: same index and same value
pos_correct = sum(1 for g, s in zip(guess, secret_list) if g == s)
# numCorrect: count matches considering multiplicity (multiset intersection)
# build counts for secret
from collections import Counter
secret_counts = Counter(secret_list)
guess_counts = Counter(guess)
multiset_intersection_count = sum(min(secret_counts[val], guess_counts[val]) for val in guess_counts)
self.numPositionCorrect = pos_correct
self.numCorrect = multiset_intersection_count
return self.numCorrect, self.numPositionCorrect
def to_dict(self):
return {
"numbers": list(self.numbers),
"numCorrect": self.numCorrect,
"numPositionCorrect": self.numPositionCorrect,
}
def __repr__(self):
return f"NumberMatch(numbers={self.numbers}, numCorrect={self.numCorrect}, numPositionCorrect={self.numPositionCorrect})"