-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathnode.go
231 lines (180 loc) · 6.24 KB
/
node.go
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package sgf
import (
"bytes"
"fmt"
"io"
)
// A Node is the fundamental unit in an SGF tree. Nodes have 0 or more keys,
// which have 1 or more values. Keys and values are strings. These strings are
// kept in an unescaped state; escaping and unescaping is handled during loading
// and saving of files. A node also contains information about the node's parent
// (if not root) and a list of all child nodes.
type Node struct {
// Properties are stored in a slice of slices of strings, where the
// first item in a slice is the key, and what follows are values.
// Instead, maps could be used, but the performance is much worse.
props [][]string // e.g. ["B" "dd"] ["TR", "dd", "fj", "np"] - e.g. 1 and 3 values for these keys.
children []*Node
parent *Node
// Note: generating a board_cache always involves generating all the ancestor
// board_caches first, so if a board_cache is nil, all the node's descendents
// will have nil caches as well. We actually rely on this fact in the method
// clear_board_cache_recursive(). Therefore, to ensure this is so, this should
// never be set directly except by a very few functions, hence its name.
__board_cache *Board
}
// NewNode creates a new node with the specified parent.
func NewNode(parent *Node) *Node {
node := new(Node)
node.parent = parent
if node.parent != nil {
node.parent.children = append(node.parent.children, node)
}
return node
}
// Copy provides a deep copy of the node with no attached parent or children.
func (self *Node) Copy() *Node {
ret := new(Node)
ret.props = make([][]string, len(self.props))
for ki := 0; ki < len(self.props); ki++ {
ret.props[ki] = make([]string, len(self.props[ki]))
for j := 0; j < len(self.props[ki]); j++ {
ret.props[ki][j] = self.props[ki][j]
}
}
return ret
}
// WriteTo writes the node in SGF format to an io.Writer. This method
// instantiates io.WriterTo for no particularly good reason.
func (self *Node) WriteTo(w io.Writer) (n int64, err error) {
b := bytes.NewBuffer(make([]byte, 0, 8)) // Start buffer with len 0 cap 8
b.WriteByte(';')
for _, slice := range self.props {
b.WriteString(slice[0])
for _, value := range slice[1:] {
b.WriteByte('[')
b.WriteString(escape_string(value))
b.WriteByte(']')
}
}
count, err := fmt.Fprint(w, b.String())
return int64(count), err
}
func (self *Node) key_index(key string) int {
for i, slice := range self.props {
if slice[0] == key {
return i
}
}
return -1
}
// ------------------------------------------------------------------------------------------------------------------
// IMPORTANT...
// AddValue(), DeleteKey(), and DeleteValue() adjust the properties directly and
// so need to call mutor_check() to see if they are affecting any cached boards.
// ------------------------------------------------------------------------------------------------------------------
// AddValue adds the specified string as a value for the given key. If the value
// already exists for the key, nothing happens.
func (self *Node) AddValue(key, val string) {
self.mutor_check(key) // If key is a MUTOR, clear board caches.
ki := self.key_index(key)
if ki == -1 {
self.props = append(self.props, []string{key, val})
return
}
for _, old_val := range self.props[ki][1:] {
if old_val == val {
return
}
}
self.props[ki] = append(self.props[ki], val)
}
// DeleteKey deletes the given key and all of its values.
func (self *Node) DeleteKey(key string) {
ki := self.key_index(key)
if ki == -1 {
return
}
self.mutor_check(key) // If key is a MUTOR, clear board caches.
self.props = append(self.props[:ki], self.props[ki + 1:]...)
}
// DeleteValue checks if the given key in this node has the given value, and
// removes that value, if it does.
func (self *Node) DeleteValue(key, val string) {
ki := self.key_index(key)
if ki == -1 {
return
}
self.mutor_check(key) // If key is a MUTOR, clear board caches.
for i := len(self.props[ki]) - 1; i >= 1; i-- { // Use i >= 1 so we don't delete the key itself.
if self.props[ki][i] == val {
self.props[ki] = append(self.props[ki][:i], self.props[ki][i + 1:]...)
}
}
// Delete key if needed...
if len(self.props[ki]) < 2 {
self.props = append(self.props[:ki], self.props[ki + 1:]...)
}
}
// ------------------------------------------------------------------------------------------------------------------
// IMPORTANT...
// The rest of the functions are either read-only, or built up from the safe
// functions above. None of these must adjust the properties directly.
// ------------------------------------------------------------------------------------------------------------------
// GetValue returns the first value for the given key, if present, in which case
// ok will be true. Otherwise it returns "" and false.
func (self *Node) GetValue(key string) (val string, ok bool) {
ki := self.key_index(key)
if ki == -1 {
return "", false
}
return self.props[ki][1], true
}
// SetValue sets the specified string as the first and only value for the given
// key.
func (self *Node) SetValue(key, val string) {
self.DeleteKey(key)
self.AddValue(key, val)
}
// SetValues sets the values of the key to the values provided. The original
// slice remains safe to modify.
func (self *Node) SetValues(key string, values []string) {
self.DeleteKey(key)
for _, val := range values {
self.AddValue(key, val)
}
}
// KeyCount returns the number of keys a node has.
func (self *Node) KeyCount() int {
return len(self.props)
}
// ValueCount returns the number of values a key has.
func (self *Node) ValueCount(key string) int {
ki := self.key_index(key)
if ki == -1 {
return 0
}
return len(self.props[ki]) - 1
}
// AllKeys returns a new slice of strings, containing all the keys that the node
// has.
func (self *Node) AllKeys() []string {
var ret []string
for _, slice := range self.props {
ret = append(ret, slice[0])
}
return ret
}
// AllValues returns a new slice of strings, containing all the values that a
// given key has in this node.
func (self *Node) AllValues(key string) []string {
ki := self.key_index(key)
if ki == -1 {
return nil
}
var ret []string // Make a new slice so that it's safe to modify.
for _, val := range self.props[ki][1:] {
ret = append(ret, val)
}
return ret
}