Add a general shuffler and deck shuffling.
This commit is contained in:
		| @@ -23,7 +23,7 @@ const ( | ||||
| // The Deck stores cards yet-to-be-dealt. | ||||
| type Deck[C StatsCollection] struct { | ||||
| 	cards []Card[C] | ||||
| 	rand  rand.Rand | ||||
| 	rand  *rand.Rand | ||||
| } | ||||
|  | ||||
| // Len returns the number of cards in the Deck. | ||||
| @@ -196,3 +196,45 @@ func (d *Deck[C]) InsertRandomRange(loFrac, hiFrac float64, card Card[C]) error | ||||
| 	errs.Add(d.Insert(slot, card)) | ||||
| 	return errs.Emit() | ||||
| } | ||||
|  | ||||
| // Shuffle completely shuffles the deck. If the deck has one or fewer cards, | ||||
| // this returns WarningTooFewCards since nothing can be shuffled. | ||||
| func (d *Deck[C]) Shuffle() error { | ||||
| 	if len(d.cards) < 2 { | ||||
| 		return WarningTooFewCards | ||||
| 	} | ||||
| 	ShuffleAll(d.cards, d.rand) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ShufflePart shuffles the `n` cards of the deck starting at `loc`. | ||||
| // If the provided range doesn't fit in the deck, this returns | ||||
| // WarningTopClamped and/or WarningBottomClamped. If the eventual range | ||||
| // of cards to be shuffled (after any off-the-end issues are corrected) | ||||
| // is one or less, this returns WarningTooFewCards since nothing can | ||||
| // be shuffled. | ||||
| func (d *Deck[C]) ShufflePart(loc, n int) error { | ||||
| 	if n < 2 { | ||||
| 		// Nothing to do. | ||||
| 		return WarningTooFewCards | ||||
| 	} | ||||
|  | ||||
| 	var errs ErrorCollector | ||||
| 	if loc < 0 { | ||||
| 		errs.Add(Warningf("%w: loc was %d", WarningTopClamped, loc)) | ||||
| 		loc = 0 | ||||
| 	} | ||||
| 	if loc+n > d.Len() { | ||||
| 		errs.Add(Warningf("%w: deck size %d does not have %d cards at and after location %d", | ||||
| 			WarningBottomClamped, len(d.cards), n, loc)) | ||||
| 		n = d.Len() - loc | ||||
| 		// Now is there anything to do? | ||||
| 		if n < 2 { | ||||
| 			errs.Add(WarningTooFewCards) | ||||
| 			return errs.Emit() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ShufflePart(d.cards, d.rand, loc, n) | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package cardsim | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math/rand" | ||||
| ) | ||||
|  | ||||
| // InsertInto inserts one or more items into a slice at an arbitrary index. | ||||
| @@ -98,3 +99,30 @@ func DeleteNFrom[T any](slice []T, loc, n int) []T { | ||||
| 	copy(slice[loc:], slice[loc+n:]) | ||||
| 	return slice[:len(slice)-n] | ||||
| } | ||||
|  | ||||
| // ShuffleAll shuffles everything in slice, using the provided rand.Rand. | ||||
| // If no rand.Rand is provided, this uses the default source. | ||||
| func ShuffleAll[T any](slice []T, r *rand.Rand) { | ||||
| 	shuffle := rand.Shuffle | ||||
| 	if r != nil { | ||||
| 		shuffle = r.Shuffle | ||||
| 	} | ||||
| 	shuffle(len(slice), func(i, j int) { | ||||
| 		slice[i], slice[j] = slice[j], slice[i] | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // ShufflePart shuffles the `n` elements of `slice` starting at `loc` | ||||
| // in-place, using the provided rand.Rand. If the range of items to | ||||
| // shuffle is not entirely within `slice`, this panics. | ||||
| // | ||||
| // If no rand.Rand is provided, this uses the default source. | ||||
| func ShufflePart[T any](slice []T, r *rand.Rand, loc, n int) { | ||||
| 	if loc < 0 || loc+n > len(slice) { | ||||
| 		panic(fmt.Sprintf("can't shuffle %d elements from a %d-element slice at location %d", n, len(slice), loc)) | ||||
| 	} | ||||
| 	if n < 1 { | ||||
| 		return | ||||
| 	} | ||||
| 	ShuffleAll(slice[loc:loc+n], r) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user