chess.js icon indicating copy to clipboard operation
chess.js copied to clipboard

How to find defended and attacked pieces?

Open vitogit opened this issue 6 years ago • 12 comments

What´s the best way to get the number of defenders and attackers for each square? I want to show in the board what pieces are unguarded and the "influence" for each square, this is the number of attackers - defenders for that square. Maybe if the legal moves method had an option to include the squares that already have an own piece it could work.

vitogit avatar Sep 14 '18 01:09 vitogit

I would like to have that functionality as well, I will give it a try. I see the following options (with different possible implementations then):

  • 2 different calls, one for defended pieces (or squares), one for attacked pieces (or squares).
  • each time the option to do that for the turn color, the other color, or both. Here an example
    • Searching for defended pieces, you have to simulate that first an attack is done, and then the defenders are computed. Currently, chess.js does not support defending moves.
    • Searching for attacking pieces, this will normally work in the current position for the color that has the turn. To get attacking pieces for the other color, you have to change the turn of the current position.

So the API could look like this:

  • attacked_pieces(color = null, attack_square = false)
    • color: default is null ==> denotes the one that has the turn.
    • BLACK | WHITE: defines for which color to search for attacked_pieces
  • defended_pieces(color) with the same semantics
  • Both return something like {'bd8': ['ne6', 'rd1'], 'qc7': ['ne6', 'bf4'], 'nf7': []} ==> Bishop on d8 is attacked by Knight on e6 and by Rook on d1 ...

mliebelt avatar Oct 29 '18 14:10 mliebelt

I did it in a hacky way and used it here https://jsfiddle.net/m5q6fgtb/859/ (When you move a piece it show number of defenders and attackers per square).

To know the numbers of defenders for my piece (Same method can be used to know the attackers), I will remove that piece and put the enemy queen and then check how many of my pieces can capture it, so that's the number of defenders. Is not tested much so maybe it's wrong,but it can help someone.

function countSquareDefenders(boardElement, fen, square, me) {   
    var chess = new Chess(fenForOtherSide(fen));

    var oppositeColor = chess.turn() == 'w' ? 'b' : 'w'
    var queenSquare = squaresOfPiece(fen, oppositeColor, 'q');
    var somePiece = chess.remove(square); // remove my piece if any
    chess.remove(queenSquare); // remove his queen
    chess.put({ // put queen in the square
        type: 'q',
        color: oppositeColor
    }, square);    
  
    var moves = chess.moves({verbose: true, legal: false})
    var defendersCount = moves.filter(m => m.to == square && m.flags =='c').length
    
    if (somePiece && somePiece.color != oppositeColor) // count our own piece as controlling that square
    	defendersCount = parseInt(defendersCount)+1
      
    return defendersCount
}

vitogit avatar Oct 29 '18 15:10 vitogit

I tried a different approach. I checked carefully the (internal) function generate_moves. All the logic is there, but by allowing defending moves, you get the moves that defend your pieces (or squares if you want). Not sure if that is the right way, but the unit tests are promising. I will hopefully commit my solution tomorrow, so you can check the pull request, if that is easier to use. The number of attackers and defenders may be sufficient sometimes, but sometimes not, so I would like to have that information complete.

mliebelt avatar Oct 29 '18 18:10 mliebelt

I'd also love to have this information. I'm not sure using generate_moves is necessarily the right approach though? It seems if there are checks involved, there could be very few valid moves, but still a rather complex "influence" map.

Should attacking and defending be based on a more naive move generation algorithm that doesn't account for check? In other words, look at all potential moves for all pieces, rather than just valid moves? Does that make any sense?

captncraig avatar Dec 27 '18 07:12 captncraig

Has there been any progress on this since *checks date* February? I am working on a chess game project and it would be awesome if there were a drop-in library that could tell what pieces are being attacked in a position. (I don't know much about this project. Can I do this with the current state of this project?)

josephcagle avatar Aug 14 '20 16:08 josephcagle

I need this so bad. I made a game that is based around select undefended pieces and I have isAttacked to see if a piece is defended, but I need to know what it's defended by because a king can't defend if the opponent threatens that square.

axolotl-logic avatar Apr 28 '23 17:04 axolotl-logic

@chao-mu I could supply something generic returning a list of attacking square. It's up to the user to filter out which pieces they don't want in the list (in your case the king). So something like:

chess.load('r1bqkbnr/ppp2ppp/2np4/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 4')

chess.attackers('d2', WHITE)
// -> ['c1', 'd1', 'e1', 'f3']

chess.attackers('d2', BLACK)
// -> []

How about the pinned knight on c6, does that get included?

chess.attackers('d4', BLACK)
// -> ['c6', 'e5']   <-- should the c6 knight be included?

How about en-passant?

jhlywa avatar Apr 28 '23 18:04 jhlywa

Whoops, deleted my post before seeing the response. There was a ton of bugs in it.

List of attacking squares would be perfect. I think attackers are only ones that can legally attack, so absolutely pinned pieces can't attack.

axolotl-logic avatar Apr 28 '23 19:04 axolotl-logic

The problem with this is that defending is not a valid move unless there is a capture.

function getAttackers(chess, square: string, color: string) {
  const attackers = Array.from(ALL_SQUARES).filter((attackerSquare) => {
    const moves = chess.moves({ square: attackerSquare, verbose: true, color: color })
    if (moves.length == 0) {
      return false    
    }                                 
    console.log(attackerSquare, square, moves.map((move) => move.to))

    return moves.some((move) => move.to == square)
  })
                        

  return new Set<string>(attackers)
}
                                                                                                                   

axolotl-logic avatar Apr 28 '23 20:04 axolotl-logic

What I ended up doing was just checking the list of legal moves for captures and using those destination squares.

As far as additions to the API, perhaps there could be separate methods to get a list of legal captures and a list of pieces “aiming” at a square (even if pinned). Maybe it would make sense to also have a list of squares a pieces is aiming at

josephcagle avatar Apr 28 '23 21:04 josephcagle

I am not concerned with captures, just defenders. I'm expecting "attackers" to return white attacking itself if specified by color

axolotl-logic avatar Apr 28 '23 21:04 axolotl-logic

Essentially I want the contract for isAttacked where I can choose the color and therefore can use it to detect defenders.

This is what I'm working on: https://tactical-elements.netlify.app/

axolotl-logic avatar Apr 28 '23 21:04 axolotl-logic