jhlywa / chess.js

A TypeScript chess library for chess move generation/validation, piece placement/movement, and check/checkmate/draw detection
BSD 2-Clause "Simplified" License
3.67k stars 888 forks source link

How to find defended and attacked pieces? #186

Open vitogit opened 6 years ago

vitogit commented 6 years ago

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.

mliebelt commented 5 years ago

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):

So the API could look like this:

vitogit commented 5 years ago

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
}
mliebelt commented 5 years ago

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.

captncraig commented 5 years ago

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?

josephcagle commented 4 years ago

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?)

chao-mu commented 1 year ago

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.

jhlywa commented 1 year ago

@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?

chao-mu commented 1 year ago

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.

chao-mu commented 1 year ago

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)
}
josephcagle commented 1 year ago

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

chao-mu commented 1 year ago

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

chao-mu commented 1 year ago

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/