github.com/frankkopp/FrankyGo@v1.0.3/internal/attacks/attacks.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 attacks 28 29 import ( 30 "github.com/op/go-logging" 31 "golang.org/x/text/language" 32 "golang.org/x/text/message" 33 34 myLogging "github.com/frankkopp/FrankyGo/internal/logging" 35 "github.com/frankkopp/FrankyGo/internal/position" 36 . "github.com/frankkopp/FrankyGo/internal/types" 37 ) 38 39 var out = message.NewPrinter(language.German) 40 41 // Attacks is a data structure to store all attacks and defends of a position. 42 type Attacks struct { 43 log *logging.Logger 44 45 // the position key for which the attacks have been calculated 46 Zobrist position.Key 47 // bitboards of attacked/defended squares for each color and each from square 48 // to get attackers us &^ ownPieces or & ownPieces for defenders 49 From [ColorLength][SqLength]Bitboard 50 // bitboards of attackers/defenders for each color and to square 51 // to get attackers us &^ ownPieces or & ownPieces for defenders 52 To [ColorLength][SqLength]Bitboard 53 // bitboards for all attacked/defended squares of a color 54 // to get attackers us &^ ownPieces or & ownPieces for defenders 55 All [ColorLength]Bitboard 56 // bitboards of attacked/defended squares for each color and each piece type 57 // to get attackers us &^ ownPieces or & ownPieces for defenders 58 Piece [ColorLength][PtLength]Bitboard 59 // sum of possible moves for each color (moves to ownPieces already excluded) 60 Mobility [ColorLength]int 61 // pawn attacks - squares attacked by pawn of the given color 62 Pawns [ColorLength]Bitboard 63 // pawn double - squares which are attacked twice by pawns of the given color 64 PawnsDouble [ColorLength]Bitboard 65 } 66 67 // NewAttacks creates a new instance of Attacks. 68 func NewAttacks() *Attacks { 69 return &Attacks{ 70 log: myLogging.GetLog(), 71 } 72 } 73 74 // Clear resets all fields of the Attacks instance without 75 // new allocation by looping through all fields 76 // This is considerably faster than creating a new instance 77 // Benchmark/New_Instance-8 1.904.764 691.0 ns/op 78 // Benchmark/Clear-8 13.043.875 91.7 ns/op. 79 func (a *Attacks) Clear() { 80 a.Zobrist = 0 81 for sq := 0; sq < SqLength; sq++ { 82 a.From[White][sq] = BbZero 83 a.From[Black][sq] = BbZero 84 a.To[White][sq] = BbZero 85 a.To[Black][sq] = BbZero 86 } 87 for pt := PtNone; pt < PtLength; pt++ { 88 a.Piece[White][pt] = BbZero 89 a.Piece[Black][pt] = BbZero 90 } 91 a.All[White] = BbZero 92 a.All[Black] = BbZero 93 a.Mobility[White] = 0 94 a.Mobility[Black] = 0 95 a.Pawns[White] = 0 96 a.Pawns[Black] = 0 97 a.PawnsDouble[White] = 0 98 a.PawnsDouble[Black] = 0 99 } 100 101 // Compute calculates all attacks on the position. 102 // Stores the positions zobrist key to be able to 103 // check if the position is already computed. 104 // if a position is called twice the already 105 // stored attacks are untouched. 106 func (a *Attacks) Compute(p *position.Position) { 107 if p.ZobristKey() == a.Zobrist { 108 a.log.Debugf("attacks compute: position was already computed") 109 return 110 } 111 a.Zobrist = p.ZobristKey() 112 a.nonPawnAttacks(p) 113 // TODO safe time with pawn hash table? 114 a.pawnAttacks(p) 115 } 116 117 // nonPawnAttacks calculates all attacks of non pawn pieces including king. 118 func (a *Attacks) nonPawnAttacks(p *position.Position) { 119 ptList := [5]PieceType{King, Knight, Bishop, Rook, Queen} 120 var attacks Bitboard 121 allPieces := p.OccupiedAll() 122 123 // iterate over colors 124 for c := White; c <= Black; c++ { 125 myPieces := p.OccupiedBb(c) 126 // iterate over all piece types 127 for _, pt := range ptList { 128 // iterate over pieces of piece type 129 for pieces := p.PiecesBb(c, pt); pieces != BbZero; { 130 psq := pieces.PopLsb() // piece square 131 // attacks will include attacks to opponents pieces 132 // and defending own pieces 133 attacks = GetAttacksBb(pt, psq, allPieces) 134 // accumulate all attacks of this piece type for the color 135 a.From[c][psq] = attacks 136 a.Piece[c][pt] |= attacks 137 a.All[c] |= attacks 138 // store all attacks to the square 139 tmp := attacks 140 for tmp != BbZero { 141 toSq := tmp.PopLsb() // attacked square 142 a.To[c][toSq].PushSquare(psq) 143 } 144 a.Mobility[c] += (attacks &^ myPieces).PopCount() 145 } 146 } 147 } 148 } 149 150 // pawnAttacks calculate all attacks for pawns. 151 func (a *Attacks) pawnAttacks(p *position.Position) { 152 a.Pawns[White] = ShiftBitboard(p.PiecesBb(White, Pawn), Northwest) | ShiftBitboard(p.PiecesBb(White, Pawn), Northeast) 153 a.Pawns[Black] = ShiftBitboard(p.PiecesBb(Black, Pawn), Northwest) | ShiftBitboard(p.PiecesBb(Black, Pawn), Northeast) 154 a.PawnsDouble[White] = ShiftBitboard(p.PiecesBb(White, Pawn), Northwest) & ShiftBitboard(p.PiecesBb(White, Pawn), Northeast) 155 a.PawnsDouble[Black] = ShiftBitboard(p.PiecesBb(Black, Pawn), Northwest) & ShiftBitboard(p.PiecesBb(Black, Pawn), Northeast) 156 } 157 158 // AttacksTo determines all attacks to the given square for the given color. 159 func AttacksTo(p *position.Position, square Square, color Color) Bitboard { 160 // prepare en passant attacks 161 epAttacks := BbZero 162 enPassantSquare := p.GetEnPassantSquare() 163 if enPassantSquare != SqNone && enPassantSquare == square { 164 pawnSquare := enPassantSquare.To(color.Flip().MoveDirection()) 165 epAttacker := pawnSquare.NeighbourFilesMask() & pawnSquare.RankOf().Bb() & p.PiecesBb(color, Pawn) 166 if epAttacker != BbZero { 167 epAttacks |= pawnSquare.Bb() 168 } 169 } 170 171 occupiedAll := p.OccupiedAll() 172 173 // this uses a reverse approach - it uses the target square as from square 174 // to generate attacks for each type and then intersects the result with 175 // the piece bitboard. 176 177 // Pawns 178 return (GetPawnAttacks(color.Flip(), square) & p.PiecesBb(color, Pawn)) | 179 // Knight 180 (GetAttacksBb(Knight, square, occupiedAll) & p.PiecesBb(color, Knight)) | 181 // King 182 (GetAttacksBb(King, square, occupiedAll) & p.PiecesBb(color, King)) | 183 // Sliding rooks and queens 184 (GetAttacksBb(Rook, square, occupiedAll) & (p.PiecesBb(color, Rook) | p.PiecesBb(color, Queen))) | 185 // Sliding bishops and queens 186 (GetAttacksBb(Bishop, square, occupiedAll) & (p.PiecesBb(color, Bishop) | p.PiecesBb(color, Queen))) | 187 // consider en passant attacks 188 epAttacks 189 } 190 191 // RevealedAttacks returns sliding attacks after a piece has been removed to reveal new attacks. 192 // It is only necessary to look at slider pieces as only their attacks can be revealed. 193 func RevealedAttacks(p *position.Position, square Square, occupied Bitboard, color Color) Bitboard { 194 // Sliding rooks and queens 195 return (GetAttacksBb(Rook, square, occupied) & (p.PiecesBb(color, Rook) | p.PiecesBb(color, Queen)) & occupied) | 196 // Sliding bishops and queens 197 (GetAttacksBb(Bishop, square, occupied) & (p.PiecesBb(color, Bishop) | p.PiecesBb(color, Queen)) & occupied) 198 }