aboutsummaryrefslogtreecommitdiff
path: root/internal/player/roomset.go
blob: 3565bb65b26ca711bbaa1675b9e9ede3f4b7d590 (plain)
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
109
package player

import (
	"encoding/base64"
	"math/bits"

	"gopkg.in/yaml.v3"
)

type RoomSet struct {
	bits []byte
}

func (rs *RoomSet) Add(roomID int) {
	if roomID < 0 {
		return
	}
	byteIdx := roomID / 8
	if byteIdx >= len(rs.bits) {
		nb := make([]byte, byteIdx+1)
		copy(nb, rs.bits)
		rs.bits = nb
	}
	rs.bits[byteIdx] |= 1 << (roomID % 8)
}

func (rs *RoomSet) Has(roomID int) bool {
	if roomID < 0 {
		return false
	}
	byteIdx := roomID / 8
	return byteIdx < len(rs.bits) && rs.bits[byteIdx]&(1<<(roomID%8)) != 0
}

func (rs *RoomSet) Len() int {
	n := 0
	for _, b := range rs.bits {
		n += bits.OnesCount8(b)
	}
	return n
}

func (rs RoomSet) IsZero() bool {
	return len(rs.bits) == 0
}

func (rs *RoomSet) Keys() []int {
	var keys []int
	for byteIdx, b := range rs.bits {
		if b == 0 {
			continue
		}
		base := byteIdx * 8
		for bitIdx := 0; bitIdx < 8; bitIdx++ {
			if b&(1<<bitIdx) != 0 {
				keys = append(keys, base+bitIdx)
			}
		}
	}
	return keys
}

func (rs *RoomSet) Rekey(idMap map[int]int) (RoomSet, bool) {
	var next RoomSet
	changed := false
	for _, k := range rs.Keys() {
		newK, ok := idMap[k]
		if !ok {
			newK = k
		} else if newK != k {
			changed = true
		}
		next.Add(newK)
	}
	return next, changed
}

func NewRoomSetFrom(ids ...int) RoomSet {
	var rs RoomSet
	for _, id := range ids {
		rs.Add(id)
	}
	return rs
}

func (rs RoomSet) MarshalYAML() (any, error) {
	return base64.StdEncoding.EncodeToString(rs.bits), nil
}

func (rs *RoomSet) UnmarshalYAML(value *yaml.Node) error {
	if value.Kind == yaml.ScalarNode && (value.Tag == "!!null" || value.Value == "null") {
		rs.bits = nil
		return nil
	}
	var s string
	if err := value.Decode(&s); err != nil {
		return err
	}
	if s == "" {
		rs.bits = nil
		return nil
	}
	b, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return err
	}
	rs.bits = b
	return nil
}