aboutsummaryrefslogtreecommitdiffstats
path: root/bingo/tiles.go
blob: 6c180178212b91f3c190c0018afc1093299d6bf5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package bingo

import (
	"iter"
	"math/rand"
	"slices"
)

type Tile struct {
	Text      string
	Checked   bool
	Checkable bool
}

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, Tile] // 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, Tile] {
	return func(yield func(string, Tile) 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
			}

			text := list[tp.rand.Intn(len(list))]
			tile := Tile{Text: text}
			if !yield(tag, tile) {
				return
			}
			yielded++
		}

	}
}

func (tp RandomTilePicker) Reset() {
	tp.ChosenTags = make([]string, len(tp.ChosenTags))
}