Drag-n-drop version of HGM's chess variant

Discussion of anything and everything relating to chess playing software and machines.

Moderators: hgm, Dann Corbit, Harvey Williamson

User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Drag-n-drop version of HGM's chess variant

Post by maksimKorzh »

hgm wrote: Fri Dec 04, 2020 4:09 pm I think I have something now that works purely with background images, and doesn't rely on browser default behavior for dragging. It works by explicitly setting the style.cursor attribute of the board <table> element to the dragged piece, by copying the cell's back-ground image there (plus some coordinates for shifting the center of the image to the pointer location, rather than the upper-left corner), and then deleting that background.

The tricky thing is to undo that when the drag ends. Problem is that not every mousedown can be a drag start. (And I cannot use ondragstart, because when the pieces are background, there is nothing in the cells to drag. So it must happen on mousedown.) In click-click moving some clicks are 'to-clicks'. And this cannot be determined in advance, because the Diagram supports re-selection: if you have selected a piece, but then click another piece that is not a highlighted destination, it assumes that you want to move (and thus drag) that piece instead. So a 'from-click' is not guaranteed to be followed by a 'to-click', it can be a new 'from-click' as well.

And if every click prepares dragging, you would have to undo it on the mouseup of a to-click as well. That is OK for the cursor (which can always be the default if no button is pressed), but when you put back the piece you deleted, it would overwrite the piece that moved there in case of a to-click. I solved that by testing at the end of the mousdown handler whether the click left a piece selected; clicks that finish a move wouldn't do that, and handling the move would in that case have put the correct piece in that square. In other cases undoing the drag start is left to the mouseup handler, when in occurs on the same square as the mousedown.

I have the following code now (working in the diagram above):

Code: Select all

var downX;
var downY;
var noMouse = 0;
var down = 0;
var drag, draggedPiece;

function Down(bnr, x, y, ev) {
  if(noMouse) return;
  SwitchDiag(bnr);
  if(ev.button == 2) {
    ev.preventDefault();
    RightClick(x, y);
    return;
  }
  downX = x; downY = y; down = 1;
  var off = sqrSize >> 1;
  drag = document.getElementById(bnr + 'y' + y + 'y' + x);
  draggedPiece = drag.style.backgroundImage;
  curCell = "";
  Click(x, y);
  if(xx < 0) drag = null; else { // attach piece to cursor if this click didn't finish move
    document.getElementById('board' + bnr).style.cursor = draggedPiece + ' ' + off + ' ' + off +', auto';
    drag.style.backgroundImage = '';
  }
}

function Up(bnr, x, y) { // catches up-click on empty square
  if(noMouse) return;
  SwitchDiag(bnr);
  if(drag) {
    document.getElementById('board' + bnr).style.cursor = 'default';
    if(down && x == downX && y == downY) drag.style.backgroundImage = draggedPiece;
    drag = null;
  }
  if(x == downX && y == downY) return; // ignore up on static click
  Click(x, y); // on other square up counts as new click event
}
It is still a bit troublesome on promotions, where the to-click does not finish the move, but where you have to select the promotion piece from the table first.
Altering cursor to piece copy is interesting idea. Even though I just don't like this artificial drag-n-drop idea, still for this project it seems to be better, however in the above diagram I can't see dragged piece (Chrome on linux) - it just shows image placeholders in the cells I'm dragging over.

re: not every mouse down is treated to be a dragstart
- event object has event.pageX and event.pageY attributes, so maybe checking for change of either of them might be a condition for actual drag start because drag start is literally mousedown event followed be cursor coordinate change.

Overall thoughts on this issue:
While having multiple chess variants sharing diagram with the same functionality it seems logical to separate UI code into a standalone library, however I see that UI manipulation code is heavily embedded into the variant itself so I clearly see the reason why you don't want to do that)
A fun fact I've realized during chess board libraries research I made before creating my own - seems like none of existing libraries contain all the possible features, e.g. fancy svg arrows and square marks/marks to show legal moves is done separately (like on lichess), drag-n-drop itself in serious chessboard libraries seems to always override default browser behavior most likely due to browser compatibility issues.

So I think you should keep your current idea of faking drag-n-drop and using background images.
After correct recognition of equivalent of drag start (mouse down + cursor coord change) your idea with cloning image to stick to mouse pointer seems to be the best possible assuming particular implementation.
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Drag-n-drop version of HGM's chess variant

Post by maksimKorzh »

maksimKorzh wrote: Fri Dec 04, 2020 4:46 pm
hgm wrote: Fri Dec 04, 2020 4:09 pm I think I have something now that works purely with background images, and doesn't rely on browser default behavior for dragging. It works by explicitly setting the style.cursor attribute of the board <table> element to the dragged piece, by copying the cell's back-ground image there (plus some coordinates for shifting the center of the image to the pointer location, rather than the upper-left corner), and then deleting that background.

The tricky thing is to undo that when the drag ends. Problem is that not every mousedown can be a drag start. (And I cannot use ondragstart, because when the pieces are background, there is nothing in the cells to drag. So it must happen on mousedown.) In click-click moving some clicks are 'to-clicks'. And this cannot be determined in advance, because the Diagram supports re-selection: if you have selected a piece, but then click another piece that is not a highlighted destination, it assumes that you want to move (and thus drag) that piece instead. So a 'from-click' is not guaranteed to be followed by a 'to-click', it can be a new 'from-click' as well.

And if every click prepares dragging, you would have to undo it on the mouseup of a to-click as well. That is OK for the cursor (which can always be the default if no button is pressed), but when you put back the piece you deleted, it would overwrite the piece that moved there in case of a to-click. I solved that by testing at the end of the mousdown handler whether the click left a piece selected; clicks that finish a move wouldn't do that, and handling the move would in that case have put the correct piece in that square. In other cases undoing the drag start is left to the mouseup handler, when in occurs on the same square as the mousedown.

I have the following code now (working in the diagram above):

Code: Select all

var downX;
var downY;
var noMouse = 0;
var down = 0;
var drag, draggedPiece;

function Down(bnr, x, y, ev) {
  if(noMouse) return;
  SwitchDiag(bnr);
  if(ev.button == 2) {
    ev.preventDefault();
    RightClick(x, y);
    return;
  }
  downX = x; downY = y; down = 1;
  var off = sqrSize >> 1;
  drag = document.getElementById(bnr + 'y' + y + 'y' + x);
  draggedPiece = drag.style.backgroundImage;
  curCell = "";
  Click(x, y);
  if(xx < 0) drag = null; else { // attach piece to cursor if this click didn't finish move
    document.getElementById('board' + bnr).style.cursor = draggedPiece + ' ' + off + ' ' + off +', auto';
    drag.style.backgroundImage = '';
  }
}

function Up(bnr, x, y) { // catches up-click on empty square
  if(noMouse) return;
  SwitchDiag(bnr);
  if(drag) {
    document.getElementById('board' + bnr).style.cursor = 'default';
    if(down && x == downX && y == downY) drag.style.backgroundImage = draggedPiece;
    drag = null;
  }
  if(x == downX && y == downY) return; // ignore up on static click
  Click(x, y); // on other square up counts as new click event
}
It is still a bit troublesome on promotions, where the to-click does not finish the move, but where you have to select the promotion piece from the table first.
Altering cursor to piece copy is interesting idea. Even though I just don't like this artificial drag-n-drop idea, still for this project it seems to be better, however in the above diagram I can't see dragged piece (Chrome on linux) - it just shows image placeholders in the cells I'm dragging over.
EDIT: on firefox it works like a charm!

re: not every mouse down is treated to be a dragstart
- event object has event.pageX and event.pageY attributes, so maybe checking for change of either of them might be a condition for actual drag start because drag start is literally mousedown event followed be cursor coordinate change.

Overall thoughts on this issue:
While having multiple chess variants sharing diagram with the same functionality it seems logical to separate UI code into a standalone library, however I see that UI manipulation code is heavily embedded into the variant itself so I clearly see the reason why you don't want to do that)
A fun fact I've realized during chess board libraries research I made before creating my own - seems like none of existing libraries contain all the possible features, e.g. fancy svg arrows and square marks/marks to show legal moves is done separately (like on lichess), drag-n-drop itself in serious chessboard libraries seems to always override default browser behavior most likely due to browser compatibility issues.

So I think you should keep your current idea of faking drag-n-drop and using background images.
After correct recognition of equivalent of drag start (mouse down + cursor coord change) your idea with cloning image to stick to mouse pointer seems to be the best possible assuming particular implementation.
User avatar
hgm
Posts: 27701
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Drag-n-drop version of HGM's chess variant

Post by hgm »

The problem is that there is nothing to drag when the pieces are background. The browser just considers it an empty table cell. So there is no pageX/pageY that will change when you press the mouse button and move. I guess the default action in that case is 'select', but that won't change the location of anything.

Currently the Interactive Diagram code is indeed very messy. Originally the routine to highlight pseudo-legal moves was way too much integrated in the UI. It now contains a second move generator that decouples them completely: it just creates a list of pseudo-legal moves. That is what I needed for the AI (a simple alpha-beta searcher). One of the things on my to-do list is to completely abandon the old move generator, and generate the move highlights from the move list. That way it will also become easier to do fully-legal highlighting.

It is also messy in that the method for embedding diagrams has evolved in time. Originally it relied on configuring it by assigning directly to the JavaScript variables in a <script> tag, and you had to put the various HTML elements (board, piece table) on the page yourself. Later I added code that would take the diagram description from the innerHTML of an element with id="diag", and which would create the board and piece table HTML element itself. Still later I made it suitable for multiple Diagrams on the same page, by scanning that page for elements with class="idiagram".

BTW, I have tried it with FireFox and Chrome on Windows now, and with FireFox on Linux. And in all cases it worked. I have no idea why you should see a placeholder image as cursor; it suggests that the URL is dead, but it is directly copied from the backgroundImage field. So if you see the piece there, I don't understand how this can happen.

I am currently working on making it possible to paste games into the Diagram, in the area (a HTML <p> tag) that normally displays the game you play with the AI. The main problem there is that it should be able to parse SAN. Thisis pretty nasty, because the Diagram normally allows the entry of illegal moves (for the purpose of easily setting up test positions), and I want it to be able to parse these as well. Also, some variants have moves that capture on a third square (like e.p. capture), which may be implied (like e.p. capture), or explicitly written in the move (e.g. Wxd4-e5).

The way I do it now is running through the list of pseudo-legal moves, to count the number of moves that match all input data (to-square, capture-square if present, piece ID if present, disambiguators if present, promotion piece if present). It also counts the number of 'exact matches', where a missing promotion suffix or third (capture) square in the notation only matches if the move in the list is not a promotion or e.p. capture. If there are no exact matches, then it substitutes the 'loose matches' for it. If this leaves multiple matches the move is ambiguous, and terminates the game. If it leaves no matches, the move is not pseudo-legal, but it could still be sufficiently specified. To figure that out it scans the board to see how many pieces match piece ID and disambiguators in the notation. If there is only a single piece of the mentioned type, or both rank and file are specified as disambiguators, then this determines the from-square of an illegal move, which is then still accepted.
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Drag-n-drop version of HGM's chess variant

Post by maksimKorzh »

hgm wrote: Fri Dec 04, 2020 5:19 pm The problem is that there is nothing to drag when the pieces are background. The browser just considers it an empty table cell. So there is no pageX/pageY that will change when you press the mouse button and move. I guess the default action in that case is 'select', but that won't change the location of anything.

Currently the Interactive Diagram code is indeed very messy. Originally the routine to highlight pseudo-legal moves was way too much integrated in the UI. It now contains a second move generator that decouples them completely: it just creates a list of pseudo-legal moves. That is what I needed for the AI (a simple alpha-beta searcher). One of the things on my to-do list is to completely abandon the old move generator, and generate the move highlights from the move list. That way it will also become easier to do fully-legal highlighting.

It is also messy in that the method for embedding diagrams has evolved in time. Originally it relied on configuring it by assigning directly to the JavaScript variables in a <script> tag, and you had to put the various HTML elements (board, piece table) on the page yourself. Later I added code that would take the diagram description from the innerHTML of an element with id="diag", and which would create the board and piece table HTML element itself. Still later I made it suitable for multiple Diagrams on the same page, by scanning that page for elements with class="idiagram".

BTW, I have tried it with FireFox and Chrome on Windows now, and with FireFox on Linux. And in all cases it worked. I have no idea why you should see a placeholder image as cursor; it suggests that the URL is dead, but it is directly copied from the backgroundImage field. So if you see the piece there, I don't understand how this can happen.

I am currently working on making it possible to paste games into the Diagram, in the area (a HTML <p> tag) that normally displays the game you play with the AI. The main problem there is that it should be able to parse SAN. Thisis pretty nasty, because the Diagram normally allows the entry of illegal moves (for the purpose of easily setting up test positions), and I want it to be able to parse these as well. Also, some variants have moves that capture on a third square (like e.p. capture), which may be implied (like e.p. capture), or explicitly written in the move (e.g. Wxd4-e5).

The way I do it now is running through the list of pseudo-legal moves, to count the number of moves that match all input data (to-square, capture-square if present, piece ID if present, disambiguators if present, promotion piece if present). It also counts the number of 'exact matches', where a missing promotion suffix or third (capture) square in the notation only matches if the move in the list is not a promotion or e.p. capture. If there are no exact matches, then it substitutes the 'loose matches' for it. If this leaves multiple matches the move is ambiguous, and terminates the game. If it leaves no matches, the move is not pseudo-legal, but it could still be sufficiently specified. To figure that out it scans the board to see how many pieces match piece ID and disambiguators in the notation. If there is only a single piece of the mentioned type, or both rank and file are specified as disambiguators, then this determines the from-square of an illegal move, which is then still accepted.
It seems like I've just found a way to access event.pageX or event.pageY, here's the workaround I'm now experimenting with:
1. create variable to store dragging state, default 0
2. on mouse down:
- dragging ^= 1
- remove background image from source
3. on mouseover
- if (dragging) // attach image clone to cursor
4. on mouse up
- dragging ^= 1

I'm now working on attaching image clone.
Let you know if succeed.
User avatar
hgm
Posts: 27701
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Drag-n-drop version of HGM's chess variant

Post by hgm »

I think I have got the SAN parser working now, so that one can also paste games into the Diagram that were played with it before.
theme=MV firstRank=1 useMarkers=1
squareSize=33
promoChoice=QNRB
shuffle=KQ,QBN
Pawn::::a2-h2
Knight:N:::b1,g1
Bishop::::c1,f1
Rook::::a1,h1
Queen::::d1
King::::e1
This was a bit tricky with shuffle games, as these have no fixed starting position. So the moves do not fully define the game. I suppose I could start such games with a FEN. For now I just print the random seed as it was before the shuffling, so that on pasting the game it can be restored, and the shuffling will recreate the same start position. The following moves can thus be pasted in the diagram above:

{1206160980} 1. d4 Ngf6 2. c3 h5 3. Ngf3 c6 4. Bd2 Bd6 5. Bd3 Rb8 6. O-O-O Nd5 7. g3 Nef6 8. Nc2 Rg8 9. Ng5 Qe8 10. Bc4 Rf8 11. e4 b5 12. Bb3 Nb6 13. e5 Nc4 14. Be3 Nxe5 15. dxe5 Bxe5 16. Nxf7 Rxf7 17. Bxf7 Qxf7