Diff
1diff --git a/matching.go b/matching.go
2index 9d2b4dbfa6bf788ac72b1f62c3c9a26e6786a278..3f5c32103ca4771cbda5e116fe9505016e44ae2b 100644
3--- a/matching.go
4+++ b/matching.go
5@@ -6,7 +6,8 @@ import (
6 )
7
8 const (
9- matchThreshold = 0.8
10+ matchThreshold = 0.8
11+ lowerThreshold = 0.3
12
13 pointsTitle = 4
14 pointsArtist = 2
15@@ -14,9 +15,20 @@ const (
16 pointsPerRound = pointsTitle + pointsArtist + pointsAlbum
17 )
18
19-func fuzzyMatch(guess, answer string) bool {
20- return similarity(normalize(guess), normalize(answer)) >= matchThreshold ||
21- similarity(normalize(withoutParentheses(guess)), normalize(withoutParentheses(answer))) >= matchThreshold
22+func fuzzyScore(guess, answer string) float64 {
23+ a := similarity(normalize(guess), normalize(answer))
24+ b := similarity(normalize(withoutParentheses(guess)), normalize(withoutParentheses(answer)))
25+ return max(a, b)
26+}
27+
28+func scoreFor(similarity float64, weight int) int {
29+ if similarity >= matchThreshold {
30+ return weight
31+ }
32+ if similarity < lowerThreshold {
33+ return 0
34+ }
35+ return int(float64(weight) * similarity + 0.5)
36 }
37
38 func withoutParentheses(s string) string {
39diff --git a/ui.go b/ui.go
40index 2d9197612ff1a9f2dc0dc2bc56668e0f2d6b8b37..79ee2a239f291b3ef3d32e0c8609d930284e27ea 100644
41--- a/ui.go
42+++ b/ui.go
43@@ -36,9 +36,11 @@ type model struct {
44 rounds int
45 played int
46 score int
47- resultParts []string
48- guesses []string
49- fieldCorrect []bool
50+ resultParts []string
51+ guesses []string
52+ fieldCorrect []bool
53+ fieldPartial []bool
54+ roundPoints int
55
56 inputs []textinput.Model
57 active int
58@@ -238,25 +240,35 @@ func (m *model) submitGuess() tea.Cmd {
59 var parts []string
60 m.guesses = make([]string, len(m.inputs))
61 m.fieldCorrect = make([]bool, len(m.inputs))
62+ m.fieldPartial = make([]bool, len(m.inputs))
63 for i, input := range m.inputs {
64 v := input.Value()
65 m.guesses[i] = v
66- if v != "" && fuzzyMatch(v, fields[i].value(m.answer)) {
67- points += fields[i].weight
68+ if v != "" {
69+ sim := fuzzyScore(v, fields[i].value(m.answer))
70+ points += scoreFor(sim, fields[i].weight)
71+ switch {
72+ case sim >= matchThreshold:
73 m.fieldCorrect[i] = true
74- parts = append(parts, fields[i].label)
75+ parts = append(parts, fields[i].label)
76+ case sim >= lowerThreshold:
77+ m.fieldPartial[i] = true
78+ }
79 }
80 }
81 if m.hinted {
82 points /= 2
83 }
84 m.score += points
85+ m.roundPoints = points
86 m.resultParts = parts
87 switch {
88 case len(parts) == 3:
89 m.resultMsg = fmt.Sprintf("Correct! +%d %s", points, pointWord(points))
90 case len(parts) > 0:
91 m.resultMsg = fmt.Sprintf("%s correct! +%d %s", strings.Join(parts, ", "), points, pointWord(points))
92+ case points > 0:
93+ m.resultMsg = fmt.Sprintf("+%d %s", points, pointWord(points))
94 default:
95 m.resultMsg = "Wrong."
96 }
97@@ -269,6 +281,7 @@ func (m *model) submitGuess() tea.Cmd {
98
99 var red = lipgloss.NewStyle().Foreground(lipgloss.Color("#cc241d"))
100 var strikeRed = lipgloss.NewStyle().Foreground(lipgloss.Color("#cc241d")).Strikethrough(true)
101+var strikeYellow = lipgloss.NewStyle().Foreground(lipgloss.Color("#d79921")).Strikethrough(true)
102 var green = lipgloss.NewStyle().Foreground(lipgloss.Color("#98971a"))
103 var yellow = lipgloss.NewStyle().Foreground(lipgloss.Color("#d79921"))
104 var orange = lipgloss.NewStyle().Foreground(lipgloss.Color("#d65a00"))
105@@ -307,7 +320,7 @@ func (m model) View() string {
106 switch {
107 case m.done:
108 style, mark = bright, ""
109- case len(m.resultParts) == 0:
110+ case len(m.resultParts) == 0 && m.roundPoints == 0:
111 style, mark = red, "✘ "
112 case m.hinted:
113 style, mark = orange, "✔ "
114@@ -330,6 +343,8 @@ func (m model) View() string {
115 b.WriteString(dim.Render(label) + green.Render(answer))
116 case m.guesses[i] == "":
117 b.WriteString(dim.Render(label) + yellow.Render(answer))
118+ case m.fieldPartial[i]:
119+ b.WriteString(dim.Render(label) + strikeYellow.Render(m.guesses[i]) + dim.Render(" ") + green.Render(answer))
120 default:
121 b.WriteString(dim.Render(label) + strikeRed.Render(m.guesses[i]) + dim.Render(" ") + green.Render(answer))
122 }