alexobviously / squares

A flexible chessboard widget for Flutter
https://pub.dev/packages/squares
Other
37 stars 15 forks source link

Skipping of move animation #22

Open Mercutio1243 opened 7 months ago

Mercutio1243 commented 7 months ago

Hi Alex,

I am experiencing a display issue with the squares chessboard when programmatically making a move. I use the makeMove command on the corresponding bishop instance and update the SquaresState. Sometimes, the move animation is skipped. As you can see in the video, this seems to be randomly on specific moves, however it then repeats when I use the undo command and repeat the specific move:

https://github.com/alexobviously/squares/assets/152015018/887fce86-ee93-46da-8322-d947e849af68

Here, it is for instance the knight move to B6, where the animation is simply skipped. I have increased the animation duration to make it more visible. As there is no difference between how I execute the different moves, I am a bit confused why this is happening sometimes.

Do you have any idea what could be the reason here? Have you faced something similar in the past?

Thanks in advance for any advise!

alexobviously commented 7 months ago

Yeah I have definitely seen this before and I did once have a quick look but didn't find anything. I'm up to have another look though. If you have any more ideas about the cause then definitely let me know.

alexobviously commented 7 months ago

By the way, I have been skim reading all the other stuff you and others have come up with in discussions, I've just been really busy with work and haven't quite had the time to get to it properly yet. It is much appreciated though and if you hit something that is really really blocking you, definitely ping me as much as you want and I'll reprioritise.

Mercutio1243 commented 7 months ago

Thanks Alex, much appreciated, and do not worry, I was able to figure out most on my own. Apart from this topic here, I do not have any open issues or questions.

I left some code for arrows in the discussions, this is more for you to consider, whether you want to include some functionality like this directly in the package. I am sure, you would want to rewrite the code a bit though ;)

Mercutio1243 commented 7 months ago

Looking into the issue, I hope I can at least give a first indication, why the skipping is happening: In my case, the issue seems to be with the variable animate in board_pieces.dart.

In board_pieces.dart, there is a if-clause to check, whether the element should be animated at that moment. During piece movement, it is called repeatedly:

if (widget.state.lastTo == id && widget.state.lastFrom != Squares.hand && symbol.isNotEmpty && widget.animatePieces && animate)

In cases, when the animation is skipped, the check always evaluates to false, because animate is always false.

implementing Next Board Move: e5
widget.state.lastTo == id: false, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: true, widget.animatePieces: true, animate: false
[repeating]
widget.state.lastTo == id: false, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: true, widget.animatePieces: true, animate: false
widget.state.lastTo == id: true, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: true, widget.animatePieces: true, animate: false
widget.state.lastTo == id: false, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: true, widget.animatePieces: true, animate: false
[repeating]
widget.state.lastTo == id: false, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: true, widget.animatePieces: true, animate: false

However, when there is no skipping observable, the animate variable is true, which allows for the entire clause to evaluate to true once:

implementNextBoardMove: Qh5
widget.state.lastTo == id: false, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: false, widget.animatePieces: true, animate: true
[repeating]
widget.state.lastTo == id: true, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: true, widget.animatePieces: true, animate: true
-----> Entire clause evaluates to true and move animation inside if-clause is returned
widget.state.lastTo == id: false, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: false, widget.animatePieces: true, animate: true
[repeating with different constellations, but never all true]
widget.state.lastTo == id: false, widget.state.lastFrom != Squares.hand: true, symbol.isNotEmpty: true, widget.animatePieces: true, animate: true
[repeating with different constellations, but never all true]

The animate variable is determined as follows:

  bool animate = true;

  @override
  void didUpdateWidget(covariant BoardPieces oldWidget) {
    // This prevents the animation from repeating in cases where it shouldn't,
    // e.g. if the board is rotated. It would also be possible to do this with
    // collection's ListEquality or something but this seems efficient.
    animate = oldWidget.state.board.join() != widget.state.board.join() && !afterDrag;
    afterDrag = false;
    super.didUpdateWidget(oldWidget);
  }

When you comment out the animate = oldWidget[...] line, there is no skipping of the animation (but the animation is also generated in cases when the user moves a piece by hand, which is not supposed to be happening).

I do not fully understand mechanic of the animate variable, but maybe this helps already. In my case from fen starting position, the skipping occurs realiably when moving pawns 2 steps forwards. Also, when queen moves sideways, but not if queen moves diagonally. In the same position it is replicable, but I cannot see a clear pattern that explains it.