1 package cribbage
2
3 import (
4 "sort"
5
6 "code.rocketnine.space/tslocum/joker"
7 )
8
9
10 type ScoringType int
11
12
13 const (
14 Peg ScoringType = 1
15 ShowHand ScoringType = 2
16 ShowCrib ScoringType = 3
17 )
18
19 func (t ScoringType) String() string {
20 switch t {
21 case Peg:
22 return "Peg"
23 case ShowHand:
24 return "ShowHand"
25 case ShowCrib:
26 return "ShowCrib"
27 default:
28 return "?"
29 }
30 }
31
32
33 type ScoreType int
34
35
36 const (
37 Score15 ScoreType = 1
38 ScorePair ScoreType = 2
39 ScoreRun ScoreType = 3
40 ScoreFlush ScoreType = 4
41 ScoreNibs ScoreType = 5
42 ScoreNobs ScoreType = 6
43 Score31 ScoreType = 7
44 ScoreGo ScoreType = 8
45 )
46
47 func (t ScoreType) String() string {
48 switch t {
49 case Score15:
50 return "15"
51 case ScorePair:
52 return "Pair"
53 case ScoreRun:
54 return "Run"
55 case ScoreFlush:
56 return "Flush"
57 case ScoreNibs:
58 return "Nibs"
59 case ScoreNobs:
60 return "Nobs"
61 case Score31:
62 return "31"
63 case ScoreGo:
64 return "Go"
65 default:
66 return "?"
67 }
68 }
69
70
71 func Score(scoringType ScoringType, c joker.Cards, starter joker.Card) (int, ScoreResults) {
72 if (scoringType == ShowHand || scoringType == ShowCrib) && (starter.Face == 0 || starter.Suit == 0) {
73 return 0, nil
74 }
75
76 var points int
77 var results ScoreResults
78 if c.Len() == 0 {
79 return points, results
80 }
81
82 var scoreCards joker.Cards
83 var permutations []joker.Cards
84 if scoringType == Peg {
85 scoreCards = c.Reversed()
86 } else {
87 scoreCards = append(c.Copy(), starter)
88 sort.Sort(scoreCards)
89 permutations = scoreCards.Permutations()
90 }
91
92
93 fifteenscore := 0
94 fifteenvalue := 0
95 if scoringType != Peg {
96 var allusedcards []joker.Cards
97 var usedcards joker.Cards
98 SCORE15:
99 for _, permhand := range permutations {
100 fifteenvalue = 0
101 usedcards = joker.Cards{}
102
103 for _, card := range permhand {
104 usedcards = append(usedcards, card)
105
106 fifteenvalue += Value(card)
107 if fifteenvalue >= 15 {
108 if fifteenvalue == 15 {
109 alreadyused := false
110 sort.Sort(usedcards)
111 for _, pastusedcards := range allusedcards {
112 if usedcards.Equal(pastusedcards) {
113 alreadyused = true
114 break
115 }
116 }
117
118 if !alreadyused {
119 allusedcards = append(allusedcards, usedcards)
120 fifteenscore++
121
122 sort.Sort(usedcards)
123 results = append(results, ScoreResult{Type: Score15, Cards: usedcards, Points: 2})
124 }
125 }
126
127 continue SCORE15
128 }
129 }
130 }
131 } else {
132 if Sum(scoreCards) == 15 {
133 results = append(results, ScoreResult{Type: Score15, Cards: scoreCards.Sorted(), Points: 2})
134 }
135 }
136
137
138 if scoringType != Peg {
139 var faces []joker.CardFace
140 SCOREPAIR:
141 for _, card := range scoreCards {
142 for _, face := range faces {
143 if face == card.Face {
144 continue SCOREPAIR
145 }
146 }
147
148 var paircards joker.Cards
149 for _, compcard := range scoreCards {
150 if compcard.Face != card.Face {
151 continue
152 }
153
154 paircards = append(paircards, compcard)
155 }
156 if len(paircards) > 1 {
157 pairmultiplier := 1
158 if len(paircards) == 3 {
159 pairmultiplier = 2
160 } else if len(paircards) == 4 {
161 pairmultiplier = 3
162 }
163 sort.Sort(paircards)
164 results = append(results, ScoreResult{Type: ScorePair, Cards: paircards, Points: len(paircards) * pairmultiplier})
165 }
166
167 faces = append(faces, card.Face)
168 }
169 } else {
170 if len(scoreCards) > 0 {
171 var pairCards joker.Cards
172 for _, compcard := range scoreCards[1:] {
173 if compcard.Face != scoreCards[0].Face {
174 break
175 }
176
177 pairCards = append(pairCards, compcard)
178 }
179 pairmultiplier := 1
180 if len(pairCards) == 2 {
181 pairmultiplier = 2
182 } else if len(pairCards) == 3 {
183 pairmultiplier = 3
184 }
185 if pairCards != nil {
186 pairCards = append(pairCards, scoreCards[0])
187 sort.Sort(pairCards)
188 results = append(results, ScoreResult{Type: ScorePair, Cards: pairCards, Points: len(pairCards) * pairmultiplier})
189 }
190 }
191 }
192
193
194 var allRunCards []joker.Cards
195 var runCards joker.Cards
196 var runScore int
197 if scoringType == Peg {
198 var compHand joker.Cards
199 var compScore int
200 var runValue int
201 runScore = 1
202
203
204
205 SCOREPEGRUN:
206 for complen := len(scoreCards); complen > 0; complen-- {
207 compHand = scoreCards[0:complen].Sorted()
208 compScore = 1
209 runCards = nil
210
211 for i, compcard := range compHand {
212 if i > 0 {
213 if int(compcard.Face) == (runValue + 1) {
214 compScore++
215 } else {
216 continue SCOREPEGRUN
217 }
218 }
219
220 runValue = int(compcard.Face)
221 }
222
223 if compScore > runScore {
224 runScore = compScore
225
226 if runScore == len(scoreCards) {
227 runCards = compHand
228 break SCOREPEGRUN
229 }
230 }
231 }
232
233 if runScore >= 3 {
234 results = append(results, ScoreResult{Type: ScoreRun, Cards: runCards, Points: runScore})
235 }
236 } else {
237 for runLength := 6; runLength > 3; runLength-- {
238 SCOREHANDRUN:
239 for _, permhand := range permutations {
240 runCards = joker.Cards{}
241
242 runScore = 0
243 for i := range permhand {
244 if i > 0 && permhand[i].Face != permhand[i-1].Face-1 {
245 break
246 }
247
248 runScore++
249 runCards = append(runCards, permhand[i])
250 }
251
252 if runScore != runLength {
253 continue
254 }
255
256 sort.Sort(runCards)
257 for _, rc := range allRunCards {
258 containsAll := true
259 for _, runCard := range runCards {
260 if !rc.Contains(runCard) {
261 containsAll = false
262 break
263 }
264 }
265 if containsAll {
266 continue SCOREHANDRUN
267 }
268 }
269
270 results = append(results, ScoreResult{Type: ScoreRun, Cards: runCards, Points: runScore})
271 allRunCards = append(allRunCards, runCards)
272 }
273 }
274 }
275
276
277 if scoringType != Peg {
278 for _, suit := range joker.StandardSuits {
279 suitvalue := 0
280 var flushCards joker.Cards
281 for _, card := range c {
282 if card.Suit == suit {
283 suitvalue++
284 flushCards = append(flushCards, card)
285 }
286 }
287 if starter.Suit == suit {
288 suitvalue++
289 flushCards = append(flushCards, starter)
290 }
291 if suitvalue == 5 || (suitvalue == 4 && scoringType == ShowHand) {
292 sort.Sort(flushCards)
293 results = append(results, ScoreResult{Type: ScoreFlush, Cards: flushCards, Points: suitvalue})
294 break
295 }
296 }
297 }
298
299
300 if scoringType != Peg {
301 rightJack := joker.Card{joker.FaceJack, starter.Suit}
302 if c.Contains(rightJack) {
303 results = append(results, ScoreResult{Type: ScoreNobs, Cards: joker.Cards{rightJack}, Points: 1})
304 }
305 }
306
307
308 if scoringType == Peg && Sum(scoreCards) == 31 {
309 sort.Sort(scoreCards)
310 results = append(results, ScoreResult{Type: Score31, Cards: scoreCards, Points: 2})
311 }
312
313 for _, r := range results {
314 points += r.Points
315 }
316 sort.Sort(results)
317 return points, results
318 }
319
View as plain text