aboutsummaryrefslogtreecommitdiffstats
path: root/bingo/tiles.go
diff options
context:
space:
mode:
Diffstat (limited to 'bingo/tiles.go')
-rw-r--r--bingo/tiles.go101
1 files changed, 101 insertions, 0 deletions
diff --git a/bingo/tiles.go b/bingo/tiles.go
new file mode 100644
index 0000000..81e945f
--- /dev/null
+++ b/bingo/tiles.go
@@ -0,0 +1,101 @@
+package bingo
+
+import (
+ "iter"
+ "math/rand"
+ "slices"
+)
+
+type TilePool map[string][]string
+
+func (pool TilePool) All() iter.Seq[string] {
+ return func(yield func(string) bool) {
+ for _, list := range pool {
+ for _, tile := range list {
+ if !yield(tile) {
+ return
+ }
+ }
+ }
+ }
+}
+
+type TilePicker interface {
+ All() iter.Seq[string] // provides an iterator over an entire TilePool
+ Iter(int) iter.Seq2[string, string] // provides an iterator over n elements of a TilePool
+ Reset() // reset the internal state of a TilePicker
+}
+
+type RandomTilePicker struct {
+ ChosenTags []string
+ tilePool TilePool
+ rand rand.Rand
+}
+
+func NewRandomTilePicker(tiles TilePool, r rand.Rand) *RandomTilePicker {
+ tp := new(RandomTilePicker)
+ tp.tilePool = tiles
+ tp.rand = r
+
+ return tp
+}
+
+// Iterate over all elements of a TilePool in a random order
+func (tp RandomTilePicker) All() iter.Seq[string] {
+ return func(yield func(string) bool) {
+ tiles := slices.Collect(tp.tilePool.All())
+ if len(tiles) == 0 {
+ return
+ }
+
+ tp.rand.Shuffle(len(tiles), func(i int, j int) {
+ tiles[i], tiles[j] = tiles[j], tiles[i]
+ })
+
+ for _, tile := range tiles {
+ if !yield(tile) {
+ return
+ }
+ }
+ }
+}
+
+// Iterator over a TilePool by choosing one tile per tag until pool is exhausted or size tiles have been yielded
+func (tp RandomTilePicker) Iter(size int) iter.Seq2[string, string] {
+ return func(yield func(string, string) bool) {
+ if len(tp.tilePool) == 0 {
+ return
+ }
+
+ tags := make([]string, len(tp.tilePool))
+ for tag := range tp.tilePool {
+ tags = append(tags, tag)
+ }
+ tp.rand.Shuffle(len(tags), func(i int, j int) {
+ tags[i], tags[j] = tags[j], tags[i]
+ })
+
+ yielded := 0
+ for _, tag := range tags {
+ if yielded == size {
+ return
+ }
+
+ list := tp.tilePool[tag]
+ if len(list) == 0 {
+ continue
+ }
+
+ tile := list[tp.rand.Intn(len(list))]
+ if !yield(tag, tile) {
+ return
+ }
+ yielded++
+ }
+
+ }
+}
+
+func (tp RandomTilePicker) Reset() {
+ tp.ChosenTags = make([]string, len(tp.ChosenTags))
+}