Kistaro Windrider
75de281cee
Stripping cards from the Hand will also be useful, so I pulled the logic of Strip out into arrayutil (more efficiently, too) and rewrote deck.Strip to use it.
148 lines
4.2 KiB
Go
148 lines
4.2 KiB
Go
package cardsim
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
)
|
|
|
|
// InsertInto inserts one or more items into a slice at an arbitrary index.
|
|
// Items already in the slice past the target position move to later positons.
|
|
//
|
|
// Like `append`, this may move the underlying array and it produces a new
|
|
// slice header (under the hood, it uses `append`). It returns the new slice
|
|
// (the original is in an undefined state and should no longer be used).
|
|
//
|
|
// If loc is negative or more than one past the end of T, Insert panics.
|
|
func InsertInto[T any](slice []T, loc int, elements ...T) []T {
|
|
if loc < 0 || loc > len(slice) {
|
|
panic(fmt.Sprintf("can't Insert at location %d in %d-element slice", loc, len(slice)))
|
|
}
|
|
|
|
// is this a no-op?
|
|
if len(elements) == 0 {
|
|
return slice
|
|
}
|
|
// is this just an append?
|
|
if loc == len(slice) {
|
|
return append(slice, elements...)
|
|
}
|
|
|
|
offset := len(elements)
|
|
oldLen := len(slice)
|
|
newSize := oldLen + offset
|
|
if newSize <= cap(slice) {
|
|
// We can reslice in place.
|
|
slice = slice[:newSize]
|
|
|
|
// Scoot trailing to their new positions.
|
|
copy(slice[loc+offset:], slice[loc:oldLen])
|
|
|
|
// Insert the new elements.
|
|
copy(slice[loc:], elements)
|
|
|
|
return slice
|
|
}
|
|
|
|
// Reallocate. Do the normal thing of doubling the size as a minimum
|
|
// when increasing space for a dynamic array; this amortizes the
|
|
// cost of repeatedly reallocating and moving the slice.
|
|
newCap := cap(slice) * 2
|
|
if newCap < newSize {
|
|
newCap = newSize
|
|
}
|
|
newSlice := make([]T, newSize, newCap)
|
|
if loc > 0 {
|
|
copy(newSlice, slice[0:loc])
|
|
}
|
|
copy(newSlice[loc:], elements)
|
|
copy(newSlice[loc+offset:], slice[loc:])
|
|
return newSlice
|
|
}
|
|
|
|
// DeleteFrom deletes an item from a slice at an arbitrary index. Items after it
|
|
// scoot up to close the gap. This returns the modified slice (like Append).
|
|
//
|
|
// If the provided location is not a valid location in the slice, this panics.
|
|
func DeleteFrom[T any](slice []T, loc int) []T {
|
|
return DeleteNFrom(slice, loc, 1)
|
|
}
|
|
|
|
// DeleteNFrom deletes N items from a slice at an arbitrary index. Items after
|
|
// it scoot up to close the gap. This returns the modified slice (like Append).
|
|
//
|
|
// If the range of items that would be deleted is not entirely valid within the
|
|
// slice, this panics.
|
|
func DeleteNFrom[T any](slice []T, loc, n int) []T {
|
|
if loc < 0 || loc+n > len(slice) {
|
|
panic(fmt.Sprintf("can't delete %d elements from a %d-element slice at location %d", n, len(slice), loc))
|
|
}
|
|
|
|
// Easy cases.
|
|
if n == 0 {
|
|
return slice
|
|
}
|
|
if loc == 0 {
|
|
return slice[n:]
|
|
}
|
|
if loc+n == len(slice) {
|
|
return slice[0:loc]
|
|
}
|
|
|
|
// Is it shorter to move up or move down?
|
|
if len(slice)-loc-n > loc {
|
|
// Move forward -- the end is big.
|
|
copy(slice[n:], slice[:loc])
|
|
return slice[n:]
|
|
}
|
|
// Move backward -- the beginnng is big or they're the same size
|
|
// (and moving backwards preserves more usable append capacity later).
|
|
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)
|
|
}
|
|
|
|
// Strip iterates T, removing any element for which removeWhen returns true
|
|
// (when provided the index of the element and the element itself as arguments).
|
|
// It returns the stripped slice.
|
|
func Strip[T any](slice []T, removeWhen func(idx int, t T) bool) []T {
|
|
if len(slice) == 0 {
|
|
return nil
|
|
}
|
|
to := 0
|
|
for from, e := range slice {
|
|
if !removeWhen(from, e) {
|
|
if to != from {
|
|
slice[to] = slice[from]
|
|
}
|
|
to++
|
|
}
|
|
}
|
|
return slice[:to]
|
|
}
|