Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use mouse to jump to any node(position) in variation window #391

Closed
6 changes: 5 additions & 1 deletion src/main/java/featurecat/lizzie/gui/LizzieFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,6 @@ public void onClicked(int x, int y) {
// check for board click
int[] boardCoordinates = boardRenderer.convertScreenToCoordinates(x, y);
int moveNumber = winrateGraph.moveNumber(x, y);

if (boardCoordinates != null) {
if (Lizzie.board.inAnalysisMode()) Lizzie.board.toggleAnalysis();
if (!isPlayingAgainstLeelaz || (playerIsBlack == Lizzie.board.getData().blackToPlay))
Expand All @@ -920,6 +919,11 @@ public void onClicked(int x, int y) {
if (Lizzie.config.showSubBoard && subBoardRenderer.isInside(x, y)) {
Lizzie.config.toggleLargeSubBoard();
}

if (Lizzie.config.showVariationGraph) {
Lizzie.frame.variationTree.jumpVariationTree(x, y);
}

repaint();
}

Expand Down
112 changes: 110 additions & 2 deletions src/main/java/featurecat/lizzie/gui/VariationTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ public class VariationTree {
private int XSPACING;
private int DOT_DIAM = 11; // Should be odd number

private ArrayList<Integer> laneUsageList;
private BoardHistoryNode curMove;
private ArrayList<Integer> laneUsageList,
laneUsageList1; // laneUsageList1 is lane used to cacalate (x,y)
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved
private BoardHistoryNode curMove, curMove1;
private int posx1, posy1, width1, height1; // Reserve a copy of varaition window axis
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved

public VariationTree() {
laneUsageList = new ArrayList<Integer>();
laneUsageList1 = new ArrayList<Integer>(); // for mouse click event
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved
}

public void drawTree(
Expand Down Expand Up @@ -134,6 +137,11 @@ public void draw(Graphics2D g, int posx, int posy, int width, int height) {
YSPACING = (Lizzie.config.showLargeSubBoard() ? 20 : 30);
XSPACING = YSPACING;

posx1 = posx;
posy1 = posy;
width1 = width;
height1 = height; // backup for mouse click event

// Draw background
g.setColor(new Color(0, 0, 0, 60));
g.fillRect(posx, posy, width, height);
Expand Down Expand Up @@ -165,4 +173,104 @@ public void draw(Graphics2D g, int posx, int posy, int width, int height) {
}
drawTree(g, posx + xoffset, curposy, 0, posy + height, node, 0, true);
}
// Clone from drawTree() method but draw nothing, just caculate (x,y)->BoardNode
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved
public BoardHistoryNode inSubtree(
int posx,
int posy,
int mouseX,
int mouseY,
int startLane,
int maxposy,
BoardHistoryNode startNode,
int variationNumber) {
// Finds depth on leftmost variation of this tree
int depth = BoardHistoryList.getDepth(startNode) + 1;
int lane = startLane;
// Figures out how far out too the right (which lane) we have to go not to collide with other
// variations
while (lane < laneUsageList1.size()
&& laneUsageList1.get(lane) <= startNode.getData().moveNumber + depth) {
// laneUsageList keeps a list of how far down it is to a variation in the different "lanes"
laneUsageList1.set(lane, startNode.getData().moveNumber - 1);
lane++;
}
if (lane >= laneUsageList1.size()) {
laneUsageList1.add(0);
}
if (variationNumber > 1) laneUsageList1.set(lane - 1, startNode.getData().moveNumber - 1);
laneUsageList1.set(lane, startNode.getData().moveNumber);

// At this point, lane contains the lane we should use (the main branch is in lane 0)

BoardHistoryNode cur = startNode;
int curposx = posx + lane * XSPACING;
int dotoffset = DOT_DIAM / 2;

// Check first node
if (Math.abs(mouseX - curposx - dotoffset) < XSPACING / 2
&& Math.abs(mouseY - posy - dotoffset) < YSPACING / 2) return startNode;

// Draw main line
while (cur.next() != null && posy + YSPACING < maxposy) {
posy += YSPACING;
cur = cur.next();
if (Math.abs(mouseX - curposx - dotoffset) < XSPACING / 2
&& Math.abs(mouseY - posy - dotoffset) < YSPACING / 2) return cur;
}
// Now we have drawn all the nodes in this variation, and has reached the bottom of this
// variation
// Move back up, and for each, draw any variations we find
while (cur.previous() != null && cur != startNode) {
cur = cur.previous();
int curwidth = lane;
BoardHistoryNode ret;
// Draw each variation, uses recursion
for (int i = 1; i < cur.numberOfChildren(); i++) {
curwidth++;
// Recursion, depth of recursion will normally not be very deep (one recursion level for
// every variation that has a variation (sort of))
if ((ret = inSubtree(posx, posy, mouseX, mouseY, curwidth, maxposy, cur.getVariation(i), i))
!= null) return ret;
}
posy -= YSPACING;
}
return null;
}

// Clone from draw() method but draw nothing, just caculate (x,y)->BoardNode
public BoardHistoryNode inVariationTree(int mouseX, int mouseY) {

// check if (x,y) is in the variation window
if (mouseX < posx1 || mouseX > posx1 + width1 || mouseY < posy1 || mouseY > posy1 + height1)
return null;

// Use dense tree for saving space if large-subboard
YSPACING = (Lizzie.config.showLargeSubBoard() ? 20 : 30);
XSPACING = YSPACING;

int middleY = posy1 + height1 / 2;
int xoffset = 30;
laneUsageList1.clear();

curMove1 = Lizzie.board.getHistory().getCurrentHistoryNode();

// Is current move a variation? If so, find top of variation
BoardHistoryNode top = BoardHistoryList.findTop(curMove1);
int curposy = middleY - YSPACING * (curMove1.getData().moveNumber - top.getData().moveNumber);
// Go to very top of tree (visible in assigned area)
BoardHistoryNode node = top;
while (curposy > posy1 + YSPACING && node.previous() != null) {
node = node.previous();
curposy -= YSPACING;
}
return inSubtree(posx1 + xoffset, curposy, mouseX, mouseY, 0, posy1 + height1, node, 0);
}

public int jumpVariationTree(int mouseX, int mouseY) {
BoardHistoryNode node;
node = inVariationTree(mouseX, mouseY); // check if (x,y) hit proper branch node
if (node == null) return -1; // check if any error occur
Lizzie.board.jumpToAnyPosition(node);
return 0;
}
}
52 changes: 52 additions & 0 deletions src/main/java/featurecat/lizzie/rules/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,58 @@ public boolean nextVariation(int idx) {
}
}

// Jump anywhere in the board history tree. Written for mouse click navigation
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved
public void jumpToAnyPosition(BoardHistoryNode targetNode) {
BoardHistoryNode srcNode, tarNode, prevNode;
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved

// tar[] to track path from target node to root node
// src[] to track path from source node to root node
int[] tar = new int[512], src = new int[512];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 512? I guess the question should be why arrays? Could we use lists instead?

Also, I would rename tar to target

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use list, but I get used of array as c programmer. This implementation could be modified to be more java style. But since it is workable, and I dont want to change it


int i = 0, j = 0, k = 0; // i is index for target node, j for source node, k is working variable
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved

// find path from target node to root node
tarNode = targetNode;
prevNode = tarNode.previous();
while (prevNode != null) {
for (k = 0; k < prevNode.numberOfChildren(); k++)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the value k is not after this loop completes I would turn it into a local variable, like you did below with m. Same comments apply for the loops below.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k is working variable, it's c style.

if (prevNode.getVariation(k) == tarNode) tar[i++] = k;
tarNode = prevNode;
prevNode = prevNode.previous();
}

// find path from source node to root node
srcNode = history.getCurrentHistoryNode();
prevNode = srcNode.previous();
while (prevNode != null) {
for (k = 0; k < prevNode.numberOfChildren(); k++)
if (prevNode.getVariation(k) == srcNode) src[j++] = k;
srcNode = prevNode;
prevNode = prevNode.previous();
}

// Compare tar[] with src[] , and try to find nearest branch
OlivierBlanvillain marked this conversation as resolved.
Show resolved Hide resolved
for (k = 0; k < Math.min(i, j); k++) {
if (tar[i - k - 1] == src[j - k - 1]) continue;
break;
}

// Move to the target node from source node
if (k == i) {
// target node is shorter, just move back
for (int m = 0; m < j - k; m++) previousMove();

} else if (k == j) {
// source node is shorter, move forward
for (int m = i - k; m > 0; m--) nextVariation(tar[m - 1]);

} else {
// in the different branchs, must move back first, then move forword
for (int m = 0; m < j - k; m++) previousMove();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you copy the two loops above, do you think there is a way to avoid that duplication?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more details? I dont understand

for (int m = i - k; m > 0; m--) nextVariation(tar[m - 1]);
}
}

/*
* Moves to next variation (variation to the right) if possible
* To move to another variation, the variation must have a move with the same move number as the current move in it.
Expand Down