commit
ad184a65b2
|
@ -0,0 +1,14 @@
|
|||
arch: amd64
|
||||
environment:
|
||||
PROJECT_NAME: 'cards-cribbage'
|
||||
CGO_ENABLED: '1'
|
||||
GO111MODULE: 'on'
|
||||
image: freebsd/latest
|
||||
packages:
|
||||
- go
|
||||
sources:
|
||||
- https://git.sr.ht/~tslocum/cards-cribbage
|
||||
tasks:
|
||||
- test: |
|
||||
cd $PROJECT_NAME
|
||||
go test ./...
|
|
@ -0,0 +1,14 @@
|
|||
arch: x86_64
|
||||
environment:
|
||||
PROJECT_NAME: 'cards-cribbage'
|
||||
CGO_ENABLED: '1'
|
||||
GO111MODULE: 'on'
|
||||
image: alpine/edge
|
||||
packages:
|
||||
- go
|
||||
sources:
|
||||
- https://git.sr.ht/~tslocum/cards-cribbage
|
||||
tasks:
|
||||
- test: |
|
||||
cd $PROJECT_NAME
|
||||
go test ./...
|
|
@ -0,0 +1,2 @@
|
|||
.idea/
|
||||
*.sh
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Trevor Slocum <trevor@rocketnine.space>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,17 @@
|
|||
# cards-cribbage
|
||||
[](https://godoc.org/git.sr.ht/~tslocum/cards-cribbage)
|
||||
[](https://builds.sr.ht/~tslocum/cards-cribbage)
|
||||
[](https://liberapay.com/rocketnine.space)
|
||||
|
||||
Cribbage rules library
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available via [godoc](https://godoc.org):
|
||||
|
||||
- [cards](https://godoc.org/git.sr.ht/~tslocum/cards)
|
||||
- [cards-cribbage](https://godoc.org/git.sr.ht/~tslocum/cards-cribbage)
|
||||
|
||||
## Support
|
||||
|
||||
Please share issues/suggestions [here](https://todo.sr.ht/~tslocum/cards-cribbage).
|
|
@ -0,0 +1,12 @@
|
|||
package cribbage
|
||||
|
||||
import "git.sr.ht/~tslocum/cards"
|
||||
|
||||
// Value returns the cribbage value of a card.
|
||||
func Value(c cards.Card) int {
|
||||
v := int(c.Face)
|
||||
if v > 10 {
|
||||
v = 10
|
||||
}
|
||||
return v
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package cribbage
|
||||
|
||||
import "git.sr.ht/~tslocum/cards"
|
||||
|
||||
// Sum returns the total cribbage value of the supplied cards.
|
||||
func Sum(c cards.Cards) int {
|
||||
var v int
|
||||
for _, card := range c {
|
||||
v += Value(card)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func compareCards(i, j interface{}) bool {
|
||||
icard := i.(cards.Card)
|
||||
jcard := j.(cards.Card)
|
||||
return icard.Value() < jcard.Value()
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module git.sr.ht/~tslocum/cards-cribbage
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
git.sr.ht/~tslocum/cards v0.1.1-0.20200114033738-f9b90e62c278
|
||||
github.com/fighterlyt/permutation v0.0.0-20170407093504-ac78aa5051ae
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
git.sr.ht/~tslocum/cards v0.1.1-0.20200114033738-f9b90e62c278 h1:WjWEU2uvliiJVeoUpjkZV/hS9f4X9qnOIS53n8lucdQ=
|
||||
git.sr.ht/~tslocum/cards v0.1.1-0.20200114033738-f9b90e62c278/go.mod h1:ABrbSXnsABGTkuJbWZsI+oGtGTOMlAC+HZy3AysOe7c=
|
||||
github.com/fighterlyt/permutation v0.0.0-20170407093504-ac78aa5051ae h1:wdS91f8H+bGgcjlx5G4LEUVXkmt/uz0VYkc6lZMIjD4=
|
||||
github.com/fighterlyt/permutation v0.0.0-20170407093504-ac78aa5051ae/go.mod h1:KqCsX+AbfYLoAjwmUkE6ocQHwto7ibjZvTY/c5QhgZg=
|
|
@ -0,0 +1,325 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package cribbage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "git.sr.ht/~tslocum/cards"
|
||||
)
|
||||
|
||||
// ScoreResult is a score from pegging or showing a hand.
|
||||
type ScoreResult struct {
|
||||
Type ScoreType
|
||||
Cards Cards
|
||||
Points int
|
||||
}
|
||||
|
||||
func (r ScoreResult) String() string {
|
||||
return fmt.Sprintf("%s for %d with %s", r.Type, r.Points, r.Cards)
|
||||
}
|
||||
|
||||
// ScoreResults is a slice of scores from pegging or showing a hand.
|
||||
type ScoreResults []ScoreResult
|
||||
|
||||
func (r ScoreResults) Len() int {
|
||||
return len(r)
|
||||
}
|
||||
|
||||
func (r ScoreResults) Swap(i, j int) {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
|
||||
func (r ScoreResults) Less(i, j int) bool {
|
||||
if r[i].Type != r[j].Type {
|
||||
return r[i].Type < r[j].Type
|
||||
} else if r[i].Points != r[j].Points {
|
||||
return r[i].Points < r[j].Points
|
||||
} else if len(r[i].Cards) != len(r[j].Cards) {
|
||||
return len(r[i].Cards) < len(r[j].Cards)
|
||||
}
|
||||
|
||||
for k := len(r[i].Cards) - 1; k >= 0; k-- {
|
||||
if r[i].Cards[k].Value() != r[j].Cards[k].Value() {
|
||||
return r[i].Cards[k].Value() < r[j].Cards[k].Value()
|
||||
}
|
||||
}
|
||||
|
||||
return i < j
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
package cribbage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "git.sr.ht/~tslocum/cards"
|
||||
)
|
||||
|
||||
var (
|
||||
testHandA = Cards{Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}
|
||||
testHandB = Cards{Card{FaceJack, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}
|
||||
testHandC = Cards{Card{Face3, SuitHearts}, Card{Face3, SuitDiamonds}, Card{Face3, SuitClubs}, Card{Face3, SuitSpades}}
|
||||
testHandD = Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}
|
||||
testHandE = Cards{Card{Face7, SuitHearts}, Card{Face8, SuitHearts}, Card{Face9, SuitHearts}, Card{Face10, SuitHearts}}
|
||||
testHandF = Cards{Card{Face3, SuitHearts}, Card{Face6, SuitHearts}, Card{Face5, SuitHearts}, Card{Face4, SuitHearts}}
|
||||
testHandG = Cards{Card{Face6, SuitHearts}, Card{Face5, SuitHearts}, Card{Face4, SuitHearts}}
|
||||
testHandH = Cards{Card{FaceAce, SuitHearts}, Card{Face3, SuitHearts}, Card{Face2, SuitHearts}, Card{Face4, SuitHearts}}
|
||||
testHandI = Cards{Card{FaceAce, SuitHearts}, Card{Face3, SuitHearts}, Card{FaceAce, SuitClubs}, Card{Face2, SuitHearts}, Card{Face4, SuitHearts}, Card{Face7, SuitHearts}}
|
||||
testHandJ = Cards{Card{Face7, SuitHearts}, Card{Face3, SuitHearts}, Card{Face2, SuitHearts}, Card{Face4, SuitHearts}}
|
||||
testHandK = Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{FaceJack, SuitSpades}}
|
||||
testHandL = Cards{Card{Face2, SuitHearts}, Card{Face2, SuitDiamonds}, Card{Face7, SuitClubs}, Card{Face7, SuitSpades}}
|
||||
testHandM = Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{FaceKing, SuitSpades}}
|
||||
)
|
||||
|
||||
type expectedPegScore struct {
|
||||
Hand Cards
|
||||
|
||||
Result []ScoreResult
|
||||
}
|
||||
|
||||
var expectedPegScores = []expectedPegScore{
|
||||
{testHandA, []ScoreResult{{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}}}},
|
||||
{testHandB, []ScoreResult{{Type: ScorePair, Points: 6, Cards: Cards{Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}}}},
|
||||
{testHandC, []ScoreResult{{Type: ScorePair, Points: 12, Cards: Cards{Card{Face3, SuitHearts}, Card{Face3, SuitDiamonds}, Card{Face3, SuitClubs}, Card{Face3, SuitSpades}}}}},
|
||||
{testHandF, []ScoreResult{{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face3, SuitHearts}, Card{Face4, SuitHearts}, Card{Face5, SuitHearts}, Card{Face6, SuitHearts}}}}},
|
||||
{testHandG, []ScoreResult{{Type: Score15, Points: 2, Cards: Cards{Card{Face4, SuitHearts}, Card{Face5, SuitHearts}, Card{Face6, SuitHearts}}}, {Type: ScoreRun, Points: 3, Cards: Cards{Card{Face4, SuitHearts}, Card{Face5, SuitHearts}, Card{Face6, SuitHearts}}}}},
|
||||
{testHandH, []ScoreResult{{Type: ScoreRun, Points: 4, Cards: Cards{Card{FaceAce, SuitHearts}, Card{Face2, SuitHearts}, Card{Face3, SuitHearts}, Card{Face4, SuitHearts}}}}},
|
||||
{testHandI, []ScoreResult{}},
|
||||
{testHandJ, []ScoreResult{{Type: ScoreRun, Points: 3}}},
|
||||
{testHandK, []ScoreResult{}},
|
||||
{testHandL, []ScoreResult{{Type: ScorePair, Points: 2, Cards: Cards{Card{Face7, SuitClubs}, Card{Face7, SuitSpades}}}}},
|
||||
{testHandM, []ScoreResult{}},
|
||||
}
|
||||
|
||||
type expectedShowScore struct {
|
||||
Starter Card
|
||||
Hand Cards
|
||||
|
||||
HandResult []ScoreResult
|
||||
CribResult []ScoreResult
|
||||
}
|
||||
|
||||
var expectedShowScores = []expectedShowScore{
|
||||
{Card{FaceAce, SuitSpades}, testHandA, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{FaceAce, SuitSpades}, Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScoreRun, Points: 5, Cards: Cards{Card{FaceAce, SuitSpades}, Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScoreFlush, Points: 5, Cards: Cards{Card{FaceAce, SuitSpades}, Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
}, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{FaceAce, SuitSpades}, Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScoreRun, Points: 5, Cards: Cards{Card{FaceAce, SuitSpades}, Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScoreFlush, Points: 5, Cards: Cards{Card{FaceAce, SuitSpades}, Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
}},
|
||||
|
||||
{Card{FaceKing, SuitClubs}, testHandA, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitSpades}, Card{FaceKing, SuitClubs}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{FaceKing, SuitClubs}}},
|
||||
{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScoreFlush, Points: 4, Cards: Cards{Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
}, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitSpades}, Card{FaceKing, SuitClubs}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{FaceKing, SuitClubs}}},
|
||||
{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face2, SuitSpades}, Card{Face3, SuitSpades}, Card{Face4, SuitSpades}, Card{Face5, SuitSpades}}},
|
||||
}},
|
||||
|
||||
{Card{Face8, SuitClubs}, testHandE, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitHearts}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitClubs}}},
|
||||
{Type: ScorePair, Points: 2, Cards: Cards{Card{Face8, SuitHearts}, Card{Face8, SuitClubs}}},
|
||||
{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitHearts}, Card{Face9, SuitHearts}, Card{Face10, SuitHearts}}},
|
||||
{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitClubs}, Card{Face9, SuitHearts}, Card{Face10, SuitHearts}}},
|
||||
{Type: ScoreFlush, Points: 4, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitHearts}, Card{Face9, SuitHearts}, Card{Face10, SuitHearts}}},
|
||||
}, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitHearts}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitClubs}}},
|
||||
{Type: ScorePair, Points: 2, Cards: Cards{Card{Face8, SuitHearts}, Card{Face8, SuitClubs}}},
|
||||
{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitHearts}, Card{Face9, SuitHearts}, Card{Face10, SuitHearts}}},
|
||||
{Type: ScoreRun, Points: 4, Cards: Cards{Card{Face7, SuitHearts}, Card{Face8, SuitClubs}, Card{Face9, SuitHearts}, Card{Face10, SuitHearts}}},
|
||||
}},
|
||||
|
||||
{Card{Face5, SuitSpades}, testHandK, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitClubs}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitSpades}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScorePair, Points: 12, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScoreNobs, Points: 1, Cards: Cards{Card{FaceJack, SuitSpades}}},
|
||||
}, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitClubs}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitSpades}, Card{FaceJack, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScorePair, Points: 12, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScoreNobs, Points: 1, Cards: Cards{Card{FaceJack, SuitSpades}}},
|
||||
}},
|
||||
|
||||
{Card{Face5, SuitSpades}, testHandM, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitClubs}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitSpades}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScorePair, Points: 12, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
}, []ScoreResult{
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitClubs}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitSpades}, Card{FaceKing, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: Score15, Points: 2, Cards: Cards{Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
{Type: ScorePair, Points: 12, Cards: Cards{Card{Face5, SuitHearts}, Card{Face5, SuitDiamonds}, Card{Face5, SuitClubs}, Card{Face5, SuitSpades}}},
|
||||
}},
|
||||
}
|
||||
|
||||
func TestPegScoring(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for i, expected := range expectedPegScores {
|
||||
if Sum(expected.Hand) > 31 {
|
||||
t.Errorf("case %d: invalid peg hand sum: got %d, want <=31: %s", i, Sum(expected.Hand), expected.Hand)
|
||||
}
|
||||
|
||||
pegPoints, pegResult := Score(Peg, expected.Hand, Card{})
|
||||
|
||||
if !resultsEqual(pegResult, expected.Result) {
|
||||
t.Fatalf("case %d: incorrect peg result: got %s, want %s: %s", i, pegResult, expected.Result, expected.Hand)
|
||||
}
|
||||
|
||||
var expectedPoints int
|
||||
for _, r := range expected.Result {
|
||||
expectedPoints += r.Points
|
||||
}
|
||||
if pegPoints != expectedPoints {
|
||||
t.Fatalf("case %d: incorrect peg score: got %d, want %d: %s", i, pegPoints, expectedPoints, expected.Hand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScoreShowHand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var expectedPoints int
|
||||
for i, expected := range expectedShowScores {
|
||||
handPoints, handResult := Score(ShowHand, expected.Hand, expected.Starter)
|
||||
|
||||
if !resultsEqual(handResult, expected.HandResult) {
|
||||
t.Fatalf("case %d: incorrect hand result: got %s, want %s: %s - %s", i, handResult, expected.HandResult, expected.Starter, expected.Hand)
|
||||
}
|
||||
|
||||
expectedPoints = 0
|
||||
for _, r := range expected.HandResult {
|
||||
expectedPoints += r.Points
|
||||
}
|
||||
if handPoints != expectedPoints {
|
||||
t.Fatalf("case %d: incorrect hand score: got %d, want %d: %s - %s", i, handPoints, expectedPoints, expected.Starter, expected.Hand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScoreShowCrib(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var expectedPoints int
|
||||
for i, expected := range expectedShowScores {
|
||||
cribPoints, cribResult := Score(ShowCrib, expected.Hand, expected.Starter)
|
||||
|
||||
if !resultsEqual(cribResult, expected.CribResult) {
|
||||
t.Fatalf("case %d: incorrect crib result: got %s, want %s: %s - %s", i, cribResult, expected.CribResult, expected.Starter, expected.Hand)
|
||||
}
|
||||
|
||||
expectedPoints = 0
|
||||
for _, r := range expected.CribResult {
|
||||
expectedPoints += r.Points
|
||||
}
|
||||
if cribPoints != expectedPoints {
|
||||
t.Fatalf("case %d: incorrect crib score: got %d, want %d: %s - %s", i, cribPoints, expectedPoints, expected.Starter, expected.Hand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resultsEqual(a []ScoreResult, b []ScoreResult) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a {
|
||||
if a[i].Type != b[i].Type {
|
||||
return false
|
||||
} else if a[i].Points != b[i].Points {
|
||||
return false
|
||||
} else if len(a[i].Cards) != len(b[i].Cards) {
|
||||
return false
|
||||
}
|
||||
|
||||
for j := range a[i].Cards {
|
||||
if !a[i].Cards[j].Equal(b[i].Cards[j]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
Loading…
Reference in New Issue