github.com/frankkopp/FrankyGo@v1.0.3/internal/moveslice/moveslice.go (about) 1 // 2 // FrankyGo - UCI chess engine in GO for learning purposes 3 // 4 // MIT License 5 // 6 // Copyright (c) 2018-2020 Frank Kopp 7 // 8 // Permission is hereby granted, free of charge, to any person obtaining a copy 9 // of this software and associated documentation files (the "Software"), to deal 10 // in the Software without restriction, including without limitation the rights 11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 // copies of the Software, and to permit persons to whom the Software is 13 // furnished to do so, subject to the following conditions: 14 // 15 // The above copyright notice and this permission notice shall be included in all 16 // copies or substantial portions of the Software. 17 // 18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 // SOFTWARE. 25 // 26 27 // Package moveslice provides helper functionality for slices 28 // of type Move (chess moves). 29 package moveslice 30 31 import ( 32 "fmt" 33 "strings" 34 "sync" 35 36 . "github.com/frankkopp/FrankyGo/internal/types" 37 ) 38 39 // MoveSlice represents a data structure (go slice) for Move. 40 type MoveSlice []Move 41 42 // NewMoveSlice creates a new move slice with the given capacity 43 // and 0 elements. 44 // Is identical to MoveSlice(make([]Move, 0, cap)) 45 func NewMoveSlice(cap int) *MoveSlice { 46 moves := make([]Move, 0, cap) 47 return (*MoveSlice)(&moves) 48 } 49 50 // Len returns the number of moves currently stored in the slice. 51 // Equivalent to len(ms) 52 func (ms *MoveSlice) Len() int { 53 return len(*ms) 54 } 55 56 // Cap returns the capacity of the slice 57 // Equivalent to cap(ms) 58 func (ms *MoveSlice) Cap() int { 59 return cap(*ms) 60 } 61 62 // PushBack appends an element at the end of the slice 63 // Equivalent to append(ms, m) 64 func (ms *MoveSlice) PushBack(m Move) { 65 *ms = append(*ms, m) 66 } 67 68 // PopBack removes and returns the move from the back of the slice. 69 // If the slice is empty, the call panics. 70 func (ms *MoveSlice) PopBack() Move { 71 if len(*ms) <= 0 { 72 panic("MoveSlice: PopBack() called on empty slice") 73 } 74 backMove := (*ms)[len(*ms)-1] 75 *ms = (*ms)[:len(*ms)-1] 76 return backMove 77 } 78 79 // PushFront prepends an element at the beginning of the slice using 80 // the underlying array (does not create a new array) 81 // Moves (copies) all elements by one index slot and adds the new move at 82 // the front. 83 func (ms *MoveSlice) PushFront(m Move) { 84 *ms = append(*ms, MoveNone) 85 copy((*ms)[1:], *ms) 86 (*ms)[0] = m 87 } 88 89 // PopFront removes and returns the move from the front of the slice. 90 // If the slice is empty, the call panics. 91 // Shrinks the capacity of the slice as it only shifts the start of 92 // the slice within the underlying array. Might lead to earlier 93 // re-allocations 94 func (ms *MoveSlice) PopFront() Move { 95 if len(*ms) <= 0 { 96 panic("MoveSlice: PopFront() called on empty slice") 97 } 98 frontMove := (*ms)[0] 99 *ms = (*ms)[1:] 100 return frontMove 101 } 102 103 // Front returns the move at the front of the slice. This is the element 104 // that would be returned by ms[0]. 105 // This call panics if the slice is empty. 106 func (ms *MoveSlice) Front() Move { 107 if len(*ms) <= 0 { 108 panic("MoveSlice: Front() called when empty") 109 } 110 return (*ms)[0] 111 } 112 113 // Back returns the move at the back of the slice. This is the element 114 // that would be returned by ms[len[ms)-1]. 115 // This call panics if the slice is empty. 116 func (ms *MoveSlice) Back() Move { 117 if len(*ms) <= 0 { 118 panic("MoveSlice: Back() called when empty") 119 } 120 return (*ms)[len(*ms)-1] 121 } 122 123 // At returns the move at index i in the slice without removing the move 124 // from the slice. At(0) refers to the first move and is the same as Front(). 125 // At(Len()-1) refers to the last move and is the same as Back(). 126 // Index will be checked against bounds and panics if out of bounds 127 func (ms *MoveSlice) At(i int) Move { 128 if len(*ms) == 0 || i < 0 || i >= len(*ms) { 129 panic("MoveSlice: Index out of bounds") 130 } 131 return (*ms)[i] 132 } 133 134 // Set puts a move at index i in the slice. Set shares the same purpose 135 // than At() but performs the opposite operation. The index i is the same 136 // index defined by At(). 137 // Index will be checked against bounds and panics if out of bounds 138 func (ms *MoveSlice) Set(i int, move Move) { 139 if len(*ms) == 0 || i < 0 || i >= len(*ms) { 140 panic("MoveSlice: Index out of bounds") 141 } 142 (*ms)[i] = move 143 } 144 145 // Filter removes all elements from the MoveSlice for 146 // which the given call to func will return false. 147 // Rebuilds the data slice by looping over all elements 148 // and only re-adding elements for which the call to the 149 // given func is true. Reuses the underlying array 150 func (ms *MoveSlice) Filter(f func(index int) bool) { 151 b := (*ms)[:0] 152 for i, x := range *ms { 153 if f(i) { 154 b = append(b, x) 155 } 156 } 157 *ms = b 158 } 159 160 // FilterCopy copies the MoveSlice into the given destination slice 161 // without the filtered elements. An element is filtered when 162 // the given call to func will return false for the element. 163 func (ms *MoveSlice) FilterCopy(dest *MoveSlice, f func(index int) bool) { 164 for i, x := range *ms { 165 if f(i) { 166 *dest = append(*dest, x) 167 } 168 } 169 } 170 171 // Clone copies the MoveSlice into a newly create MoveSlice 172 // doing a deep copy. 173 func (ms *MoveSlice) Clone() *MoveSlice { 174 dest := make([]Move, ms.Len(), ms.Cap()) 175 copy(dest, *ms) 176 return (*MoveSlice)(&dest) 177 } 178 179 // Equals returns true if all elements of the MoveSlice equals 180 // the elements of the other MoveSlice 181 func (ms *MoveSlice) Equals(other *MoveSlice) bool { 182 if ms.Len() != other.Len() { 183 return false 184 } 185 for i, m := range *ms { 186 if m != (*other)[i] { 187 return false 188 } 189 } 190 return true 191 } 192 193 // ForEach simple range loop calling the given function on each element 194 // in stored order 195 func (ms *MoveSlice) ForEach(f func(index int)) { 196 for index := range *ms { 197 f(index) 198 } 199 } 200 201 // ForEachParallel simple loop over all elements calling a goroutine 202 // which calls the given func with the index of the current element 203 // as a parameter. 204 // Waits until all elements have been processed. There is no 205 // synchronization for the parallel execution. This needs to done 206 // in the provided function if necessary 207 func (ms *MoveSlice) ForEachParallel(f func(index int)) { 208 sliceLength := len(*ms) 209 var wg sync.WaitGroup 210 wg.Add(sliceLength) 211 for index := range *ms { 212 go func(i int) { 213 defer wg.Done() 214 f(i) 215 }(index) 216 } 217 wg.Wait() 218 } 219 220 // Clear removes all moves from the slice, but retains the current capacity. 221 // This is useful when repeatedly reusing the slice at high frequency to avoid 222 // GC during reuse. 223 func (ms *MoveSlice) Clear() { 224 // *ms = nil 225 *ms = (*ms)[:0] 226 } 227 228 // Sort will sort moves from highest Value to lowest Value . 229 // It uses a stable InsertionSort as MoveSlices are mostly pre-sorted and small. 230 // Sorts move only on the base of their Value (Move value == move&0xFFFF0000). 231 // Otherwise the order will not be changed. 232 func (ms *MoveSlice) Sort() { 233 l := len(*ms) 234 for i := 1; i < l; i++ { 235 tmp := (*ms)[i] 236 j := i 237 for j > 0 && (tmp&0xFFFF0000) > ((*ms)[j-1]&0xFFFF0000) { 238 (*ms)[j] = (*ms)[j-1] 239 j-- 240 } 241 (*ms)[j] = tmp 242 } 243 } 244 245 // String returns a string representation of a slice of moves 246 func (ms *MoveSlice) String() string { 247 var os strings.Builder 248 size := len(*ms) 249 os.WriteString(fmt.Sprintf("MoveList: [%d] { ", size)) 250 for i := 0; i < size; i++ { 251 if i > 0 { 252 os.WriteString(", ") 253 } 254 m := ms.At(i) 255 os.WriteString(m.String()) 256 } 257 os.WriteString(" }") 258 return os.String() 259 } 260 261 // StringUci returns a string with a space separated list 262 // of all moves in the list in UCI protocol format 263 func (ms *MoveSlice) StringUci() string { 264 var os strings.Builder 265 size := len(*ms) 266 for i := 0; i < size; i++ { 267 if i > 0 { 268 os.WriteString(" ") 269 } 270 m := (*ms)[i] 271 os.WriteString(m.StringUci()) 272 } 273 return os.String() 274 }