joker-cribbage/score.go

326 lines
7.0 KiB
Go

package cribbage
import (
"log"
"sort"
. "git.sr.ht/~tslocum/cards"
"github.com/fighterlyt/permutation"
)
// ScoringType represents a set of scoring rules.
type ScoringType int
// Scoring types
const (
Peg ScoringType = 1
ShowHand ScoringType = 2
ShowCrib ScoringType = 3
)
func (t ScoringType) String() string {
switch t {
case Peg:
return "Peg"
case ShowHand:
return "ShowHand"
case ShowCrib:
return "ShowCrib"
default:
return "?"
}
}
// ScoreType represents a type of score.
type ScoreType int
// Score types and their point values
const (
Score15 ScoreType = 1 // 2
ScorePair ScoreType = 2 // 2
ScoreRun ScoreType = 3 // 1/card
ScoreFlush ScoreType = 4 // 1/card
ScoreNibs ScoreType = 5 // 2
ScoreNobs ScoreType = 6 // 1
Score31 ScoreType = 7 // 2
ScoreGo ScoreType = 8 // 1
)
func (t ScoreType) String() string {
switch t {
case Score15:
return "15"
case ScorePair:
return "Pair"
case ScoreRun:
return "Run"
case ScoreFlush:
return "Flush"
case ScoreNibs:
return "Nibs"
case ScoreNobs:
return "Nobs"
case Score31:
return "31"
case ScoreGo:
return "Go"
default:
return "?"
}
}
// Score returns the score of a pegging play or shown hand.
func Score(scoringType ScoringType, c Cards, starter Card) (int, ScoreResults) {
if (scoringType == ShowHand || scoringType == ShowCrib) && (starter.Face == 0 || starter.Suit == 0) {
return 0, nil
}
var points int
var results ScoreResults
if c.Len() == 0 {
return points, results
}
var scoreCards Cards
if scoringType == Peg {
scoreCards = c.Reverse()
} else {
scoreCards = append(c.Copy(), starter).Sort()
}
// Score 15s
fifteenscore := 0
fifteenvalue := 0
if scoringType != Peg {
var allusedcards []Cards
var usedcards Cards
perm, err := permutation.NewPerm(scoreCards, compareCards)
if err != nil {
log.Panicf("failed to generate a permutation of cards %s", scoreCards)
}
SCORE15:
for permhand, err := perm.Next(); err == nil; permhand, err = perm.Next() {
fifteenvalue = 0
permhand := permhand.(Cards)
usedcards = Cards{}
for _, card := range permhand {
usedcards = append(usedcards, card)
fifteenvalue += Value(card)
if fifteenvalue >= 15 {
if fifteenvalue == 15 {
alreadyused := false
sort.Sort(usedcards)
for _, pastusedcards := range allusedcards {
if usedcards.Equal(pastusedcards) {
alreadyused = true
break
}
}
if !alreadyused {
allusedcards = append(allusedcards, usedcards)
fifteenscore++
results = append(results, ScoreResult{Type: Score15, Cards: usedcards.Sort(), Points: 2})
}
}
continue SCORE15
}
}
}
} else {
if Sum(scoreCards) == 15 {
results = append(results, ScoreResult{Type: Score15, Cards: scoreCards.Sort(), Points: 2})
}
}
// Score pairs
if scoringType != Peg {
var faces []CardFace
SCOREPAIR:
for _, card := range scoreCards {
for _, face := range faces {
if face == card.Face {
continue SCOREPAIR
}
}
var paircards Cards
for _, compcard := range scoreCards {
if compcard.Face != card.Face {
continue
}
paircards = append(paircards, compcard)
}
if len(paircards) > 1 {
pairmultiplier := 1
if len(paircards) == 3 {
pairmultiplier = 2
} else if len(paircards) == 4 {
pairmultiplier = 3
}
results = append(results, ScoreResult{Type: ScorePair, Cards: paircards.Sort(), Points: len(paircards) * pairmultiplier})
}
faces = append(faces, card.Face)
}
} else {
if len(scoreCards) > 0 {
var paircards Cards
for _, compcard := range scoreCards[1:] {
if compcard.Face != scoreCards[0].Face {
break
}
paircards = append(paircards, compcard)
}
pairmultiplier := 1
if len(paircards) == 2 {
pairmultiplier = 2
} else if len(paircards) == 3 {
pairmultiplier = 3
}
if paircards != nil {
results = append(results, ScoreResult{Type: ScorePair, Cards: append(paircards, scoreCards[0]).Sort(), Points: (len(paircards) + 1) * pairmultiplier})
}
}
}
// Score runs
var allRunCards []Cards
var runCards Cards
var runScore int
if scoringType == Peg {
var compHand Cards
var compScore int
var runValue int
runScore = 1
// Examine the pile for a run by shortening the checked pile one card
// after each iteration.
SCOREPEGRUN:
for complen := 0; complen < len(scoreCards); complen++ {
compHand = scoreCards[0 : len(scoreCards)-complen]
compScore = 1
runCards = nil
for i, compcard := range compHand.Sort() {
if i > 0 {
if int(compcard.Face) == (runValue + 1) {
compScore++
} else {
continue SCOREPEGRUN
}
}
runValue = int(compcard.Face)
}
if compScore > runScore {
runScore = compScore
if runScore == len(scoreCards) {
runCards = compHand.Sort()
break SCOREPEGRUN
}
}
}
if runScore >= 3 {
results = append(results, ScoreResult{Type: ScoreRun, Cards: runCards, Points: runScore})
}
} else {
for runLength := 6; runLength > 3; runLength-- {
perm, err := permutation.NewPerm(scoreCards, compareCards)
if err != nil {
log.Panicf("failed to generate a permutation of cards %s", scoreCards)
}
SCOREHANDRUN:
for permhand, err := perm.Next(); err == nil; permhand, err = perm.Next() {
permhand := permhand.(Cards)
runCards = Cards{}
runScore = 0
for i := range permhand {
if i > 0 && permhand[i].Face != permhand[i-1].Face-1 {
break
}
runScore++
runCards = append(runCards, permhand[i])
}
if runScore != runLength {
continue
}
runCards = runCards.Sort()
for _, rc := range allRunCards {
containsAll := true
for _, runCard := range runCards {
if !rc.Contains(runCard) {
containsAll = false
break
}
}
if containsAll {
continue SCOREHANDRUN
}
}
results = append(results, ScoreResult{Type: ScoreRun, Cards: runCards, Points: runScore})
allRunCards = append(allRunCards, runCards)
}
}
}
// Score flushes
if scoringType != Peg {
for _, suit := range StandardSuits {
suitvalue := 0
var flushCards Cards
for _, card := range c {
if card.Suit == suit {
suitvalue++
flushCards = append(flushCards, card)
}
}
if starter.Suit == suit {
suitvalue++
flushCards = append(flushCards, starter)
}
if suitvalue == 5 || (suitvalue == 4 && scoringType == ShowHand) {
results = append(results, ScoreResult{Type: ScoreFlush, Cards: flushCards.Sort(), Points: suitvalue})
break
}
}
}
// Score nobs
if scoringType != Peg {
rightJack := Card{FaceJack, starter.Suit}
if c.Contains(rightJack) {
results = append(results, ScoreResult{Type: ScoreNobs, Cards: Cards{rightJack}, Points: 1})
}
}
// Score 31
if scoringType == Peg && Sum(scoreCards) == 31 {
results = append(results, ScoreResult{Type: Score31, Cards: scoreCards.Sort(), Points: 2})
}
for _, r := range results {
points += r.Points
}
sort.Sort(results)
return points, results
}