-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathboard_cache.go
137 lines (105 loc) · 3.5 KB
/
board_cache.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
package sgf
// Note: boards are created only as needed, and some SGF manipulation
// can be done creating no boards whatsoever.
var mutors = []string{"B", "W", "AB", "AW", "AE", "PL", "SZ"}
var total_board_updates int // For debugging.
// -----------------------------------------------------------------------------------------------
// clear_board_cache_recursive() needs to be called whenever a node's board cache becomes invalid.
// This can be due to:
//
// * Changing a board-altering property.
// * Changing the identity of its parent.
func (self *Node) clear_board_cache_recursive() {
if self.__board_cache == nil { // If nil, all descendent caches are nil also.
return // See note in the Node struct about this.
}
self.__board_cache = nil
for _, child := range self.children {
child.clear_board_cache_recursive()
}
}
func (self *Node) mutor_check(key string) {
// If the key changes the board, all descendent boards are also invalid.
for _, s := range mutors {
if key == s {
self.clear_board_cache_recursive()
break
}
}
}
// -----------------------------------------------------------------------------------------------
// Board uses the entire history of the tree up to this point to return a board.
// A copy of the result is cached intelligently; the cached board is also purged
// automatically if it becomes invalid (e.g. because a board-altering property
// changed in a relevant part of the SGF tree). Note that modifying a board has
// no effect on the SGF node which created it.
func (self *Node) Board() *Board {
// Return cache if it exists...
if self.__board_cache != nil {
return self.__board_cache.Copy()
}
// Generate without recursion... also filling in any empty ancestor caches on the way.
// This is essential, see note in Node struct about this.
line := self.GetLine()
var initial, work *Board
for _, node := range line {
if node.__board_cache != nil {
initial = node.__board_cache // Care: points to the real thing, not a copy!
continue
}
if work == nil {
if initial == nil {
work = NewBoard(node.RootBoardSize())
} else {
work = initial.Copy() // MUST COPY
}
}
work.update_from_node(node)
node.__board_cache = work.Copy()
}
// At this point, work is never nil. It is safe to return work itself since we
// only stored copies of it in the cache.
return work
}
func (self *Board) update_from_node(node *Node) {
total_board_updates++
// AB, AW, and AE are updated with AddStone() or AddList() which can create illegal
// positions; this is normal according to the specs. Ko is cleared, next player is updated.
for _, p := range node.AllValues("AB") {
if len(p) == 5 && p[2] == ':' {
self.AddList(p, BLACK)
} else {
self.AddStone(p, BLACK)
}
}
for _, p := range node.AllValues("AW") {
if len(p) == 5 && p[2] == ':' {
self.AddList(p, WHITE)
} else {
self.AddStone(p, WHITE)
}
}
for _, p := range node.AllValues("AE") {
if len(p) == 5 && p[2] == ':' {
self.AddList(p, EMPTY)
} else {
self.AddStone(p, EMPTY)
}
}
// B and W are updated with ForceStone(), which has no legality checks but does
// perform captures, as well as swapping the next player and setting the ko square.
for _, p := range node.AllValues("B") {
self.ForceStone(p, BLACK)
}
for _, p := range node.AllValues("W") {
self.ForceStone(p, WHITE)
}
// Respect PL property
pl, _ := node.GetValue("PL")
if pl == "B" || pl == "b" {
self.Player = BLACK
}
if pl == "W" || pl == "w" {
self.Player = WHITE
}
}