...

Source file src/code.rocketnine.space/tslocum/joker-cribbage/score.go

Documentation: code.rocketnine.space/tslocum/joker-cribbage

     1  package cribbage
     2  
     3  import (
     4  	"sort"
     5  
     6  	"code.rocketnine.space/tslocum/joker"
     7  )
     8  
     9  // ScoringType represents a set of scoring rules.
    10  type ScoringType int
    11  
    12  // Scoring types
    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  // ScoreType represents a type of score.
    33  type ScoreType int
    34  
    35  // Score types and their point values
    36  const (
    37  	Score15    ScoreType = 1 // 2
    38  	ScorePair  ScoreType = 2 // 2
    39  	ScoreRun   ScoreType = 3 // 1/card
    40  	ScoreFlush ScoreType = 4 // 1/card
    41  	ScoreNibs  ScoreType = 5 // 2
    42  	ScoreNobs  ScoreType = 6 // 1
    43  	Score31    ScoreType = 7 // 2
    44  	ScoreGo    ScoreType = 8 // 1
    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  // Score returns the score of a pegging play or shown hand.
    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  	// Score 15s
    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  	// Score pairs
   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  	// Score runs
   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  		// Examine the pile for a run by shortening the checked pile one card
   204  		// after each iteration.
   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  	// Score flushes
   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  	// Score nobs
   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  	// Score 31
   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