summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Dailly <sebastien@chimrod.com>2014-09-27 12:13:38 +0200
committerSébastien Dailly <sebastien@chimrod.com>2014-09-27 12:13:38 +0200
commit5049d4c8a05d3f9b72f4c7e048e94b753713beda (patch)
treeda43b04f8eec2309ae16a9869bc92a0be055e424
parentaceb0d301274962289d62a41421e3cf0c8738053 (diff)
Added navigation tree management
-rw-r--r--qml/javascript/goban_util.js28
-rw-r--r--qml/javascript/navigator.js134
-rw-r--r--qml/pages/Goban.qml99
-rw-r--r--qml/python/game.py2
-rw-r--r--qml/python/tests/test2.sgf6
-rw-r--r--qml/python/tests/test_transformations.py14
-rw-r--r--qml/python/transformations.py29
7 files changed, 282 insertions, 30 deletions
diff --git a/qml/javascript/goban_util.js b/qml/javascript/goban_util.js
index eec6392..333959e 100644
--- a/qml/javascript/goban_util.js
+++ b/qml/javascript/goban_util.js
@@ -64,7 +64,7 @@ function getChainToRemove(index, grid, filter) {
* filter wich keep only free places.
*/
function freePlaces(x) {
- return grid.getElementAtIndex(x).getType() === "";
+ return grid.itemAt(x).getType() === "";
}
var piece = index;
@@ -72,8 +72,8 @@ function getChainToRemove(index, grid, filter) {
/* if the case has already been marked, do not check it again.
*/
- if (!grid.getElementAtIndex(piece).mark) {
- grid.getElementAtIndex(piece).mark = true;
+ if (!grid.itemAt(piece).mark) {
+ grid.itemAt(piece).mark = true;
piecesToRemove.push(piece);
var neighbors = getNeighbors(piece, grid.columns, grid.rows);
@@ -128,7 +128,7 @@ function undo(grid, step) {
* filter wich keep only free places.
*/
function freePlaces(x) {
- return grid.getElementAtIndex(x).getType() === "";
+ return grid.itemAt(x).getType() === "";
}
var piece = index;
@@ -137,7 +137,7 @@ function undo(grid, step) {
while (piece !== undefined) {
- var point = grid.getElementAtIndex(piece);
+ var point = grid.itemAt(piece);
if (point.mark || !point.getType === "") {
piece = space.pop();
@@ -167,7 +167,7 @@ function undo(grid, step) {
fillWith(point, !step.player);
});
}
- grid.getElementAtIndex(step.added).remove(false);
+ grid.itemAt(step.added).remove(false);
}
clearMarks(grid);
@@ -182,7 +182,7 @@ function undo(grid, step) {
* grid(object): the grid where to put the stone:
* - grid.rows: number of rows in the grid
* - grid.columns: number of columes in the grid
- * - grid.getElementAtIndex(index) should return the stone a the given index
+ * - grid.itemAt(index) should return the stone a the given index
* currentPlayer(bool): player color
* animation(bool): should we add animation on the goban
* allowSuicide(bool): if suicide an autorized action
@@ -191,7 +191,7 @@ function undo(grid, step) {
*/
function addPiece(index, grid, currentPlayer, animation, allowSuicide, allowOveride) {
- var point = grid.getElementAtIndex(index);
+ var point = grid.itemAt(index);
var elementType = point.getType();
if (!allowOveride && elementType !== "") {
@@ -205,15 +205,15 @@ function addPiece(index, grid, currentPlayer, animation, allowSuicide, allowOver
step.player = currentPlayer;
function isPlayer(x) {
- return grid.getElementAtIndex(x).getType() === (currentPlayer ? "white" : "black");
+ return grid.itemAt(x).getType() === (currentPlayer ? "white" : "black");
}
function isOponnent(x) {
- return grid.getElementAtIndex(x).getType() === (currentPlayer ? "black" : "white");
+ return grid.itemAt(x).getType() === (currentPlayer ? "black" : "white");
}
function freeOrChain(x) {
- var pointType = grid.getElementAtIndex(x).getType();
+ var pointType = grid.itemAt(x).getType();
return pointType === "" || pointType === (currentPlayer ? "white" : "black");
}
@@ -241,7 +241,7 @@ function addPiece(index, grid, currentPlayer, animation, allowSuicide, allowOver
step.removed.push(neighbor);
somethingToRemove = true;
piecesToRemove.forEach(function(x) {
- grid.getElementAtIndex(x).remove(animation);
+ grid.itemAt(x).remove(animation);
});
}
});
@@ -257,7 +257,7 @@ function addPiece(index, grid, currentPlayer, animation, allowSuicide, allowOver
step.suicide = true;
suicides.forEach(function(x) {
- grid.getElementAtIndex(x).remove(animation);
+ grid.itemAt(x).remove(animation);
});
} else {
point.remove(false);
@@ -289,6 +289,6 @@ function addPiece(index, grid, currentPlayer, animation, allowSuicide, allowOver
*/
function clearMarks(grid) {
for (var i = 0; i < grid.columns * grid.rows; i++) {
- grid.getElementAtIndex(i).mark = false;
+ grid.itemAt(i).mark = false;
}
}
diff --git a/qml/javascript/navigator.js b/qml/javascript/navigator.js
new file mode 100644
index 0000000..984b149
--- /dev/null
+++ b/qml/javascript/navigator.js
@@ -0,0 +1,134 @@
+.pragma library
+
+function getCurrentAction(path, tree) {
+
+ var way = tree;
+ var ended = false;
+
+ /*
+ * Get the action pointed by the path.
+ */
+ path.forEach( function(element, index, array) {
+ if (!ended && element < way.length) {
+ way = way[element];
+ } else {
+ ended = true;
+ }
+ });
+
+ if ( !ended ) {
+ return way;
+ } else {
+ return undefined;
+ }
+
+}
+
+function checkAction(path, tree, player, playedPosition) {
+
+ var way = getCurrentAction(path, tree);
+ var pathIndex;
+
+ if (Array.isArray(way)) {
+ /*
+ * We have choice between different possibilities.
+ * We check each of them to get the player action.
+ */
+ if (way.some( function(element, index, array) {
+
+ /*
+ * Increment the path to the next position, and check the expected
+ * result.
+ */
+ path.push(index);
+ var next = getCurrentAction(path, tree)[0];
+
+ console.log( next );
+
+ var expectedIndex;
+ if (player) {
+ expectedIndex = next.W[0];
+ console.log("W", next.W);
+ } else {
+ expectedIndex = next.B[0];
+ console.log("B", next.B);
+ }
+
+ if (playedPosition === expectedIndex) {
+ path.push(0);
+ return true;
+ }
+
+ /*
+ * The position was not the expected one. Restore the path to the
+ * original one.
+ */
+ path.pop();
+ return false;
+
+ })) {
+ /*
+ * We got the rigth action. Now, get the next position in the path.
+ */
+ console.log("Good !!");
+ pathIndex = path.length - 1;
+ path[pathIndex] = path[pathIndex] + 1;
+ return path;
+ } else {
+ /*
+ * The played position does not match the recorded game.
+ */
+ return undefined;
+ }
+
+ } else {
+
+ /*
+ * We only have one possibility, return it.
+ */
+ console.log("Single result", way);
+
+ var move;
+ if (player) {
+ move = way.W[0];
+ } else {
+ move = way.B[0];
+ }
+
+ if (move === playedPosition) {
+ console.log("Good !!", path);
+ pathIndex = path.length - 1;
+ path[pathIndex] = path[pathIndex] + 1;
+ return path;
+ } else {
+ return undefined;
+ }
+ }
+
+}
+
+function play(path, tree, player, addPiece) {
+
+ var way = getCurrentAction(path, tree);
+ var pathIndex;
+
+ if (Array.isArray(way)) {
+
+ } else {
+
+ var move;
+ if (!player) {
+ move = way.W[0];
+ } else {
+ move = way.B[0];
+ }
+ pathIndex = path.length - 1;
+ path[pathIndex] = path[pathIndex] + 1;
+ addPiece(move, path);
+ }
+}
+
+function undo(path) {
+ var way = getCurrentAction(path, tree);
+
+}
diff --git a/qml/pages/Goban.qml b/qml/pages/Goban.qml
index c1e81bd..66002f7 100644
--- a/qml/pages/Goban.qml
+++ b/qml/pages/Goban.qml
@@ -1,6 +1,7 @@
import QtQuick 2.0
import "../javascript/goban_util.js" as Actions
+import "../javascript/navigator.js" as TreeNavigator
Item {
@@ -18,6 +19,8 @@ Item {
property bool limitLeft: true;
property bool limitRight: true;
+ property bool completed: false;
+
/*
* The current color to play :
* - true for white
@@ -25,8 +28,23 @@ Item {
*/
property bool currentPlayer: true;
+ property bool initialPlayer: true;
+
+ property bool freePlay: false;
+
+ /**
+ * The game tree.
+ */
property variant tree;
+ /**
+ * Path in the tree.
+ */
+ property variant path;
+
+ /*
+ * History for cancelling a move.
+ */
property variant history;
/*
@@ -36,13 +54,14 @@ Item {
*/
function start() {
- //currentPlayer = true;
+ completed = false;
for (var i = 0; i < goban.rows * goban.columns; i++) {
repeater.itemAt(i).remove(false);
}
- var initial
+ var initial;
+ currentPlayer = initialPlayer;
i = 0;
@@ -52,18 +71,19 @@ Item {
initial = tree[i];
history = [];
+ path = [i + 1];
var aw = initial.AW;
if (aw !== undefined) {
aw.forEach(function (pos) {
- goban.getItemAt(pos[0], pos[1]).put(currentPlayer, false);
+ goban.itemAt(pos).put(true, false);
});
}
var ab = initial.AB;
if (ab !== undefined) {
ab.forEach(function (pos) {
- goban.getItemAt(pos[0], pos[1]).put(!currentPlayer, false);
+ goban.itemAt(pos).put(false, false);
});
}
}
@@ -88,8 +108,8 @@ Item {
caseSize = maxWidth;
}
- console.log("Current player:", ret.current_player);
- currentPlayer = (ret.current_player === 'W');
+ initialPlayer = (ret.current_player === 'W');
+ console.log(ret.current_player);
/*
* Put the initials stones
@@ -107,11 +127,17 @@ Item {
}
var currentHistory = history;
- var step = currentHistory.pop();
- history = currentHistory;
- Actions.undo(goban, step);
- currentPlayer = step.player;
+ var actions = currentHistory.pop();
+ actions.reverse();
+
+ actions.forEach(function (x) {
+ Actions.undo(goban, x);
+ currentPlayer = x.player;
+ path = x.path;
+ });
+
+ history = currentHistory;
}
/**
@@ -119,6 +145,10 @@ Item {
*/
function clickHandler(index) {
+ if (completed) {
+ return;
+ }
+
if ( (!limitLeft && Actions.isFirstCol(index, goban.columns))
|| (!limitRight && Actions.isLastCol(index, goban.columns))
|| (!limitTop && Actions.isFirstRow(index, goban.columns))
@@ -130,11 +160,52 @@ Item {
var step = Actions.addPiece(index, goban, currentPlayer, true, false, false);
if (step !== undefined) {
- currentPlayer = !currentPlayer;
+
+ /*
+ * Update the path.
+ */
+ var currentPosition = path[path.length - 1];
+ step.path = path;
+ var action = TreeNavigator.checkAction(path, tree, currentPlayer, index);
+ path = action;
+
+
+ /*
+ * Update the history with the last move.
+ */
var currentHistory = history;
- currentHistory.push(step);
+ var actions = [step];
+
+
+ if (action === undefined) {
+ /*
+ * Switch to variation mode
+ */
+ } else {
+ if (TreeNavigator.getCurrentAction(action, tree) === undefined) {
+ console.log("Level completed!");
+ completed = true;
+ return;
+ } else {
+ /*
+ * Play the openent move.
+ */
+
+ TreeNavigator.play(action, tree, currentPlayer, function(x, newPath) {
+ console.log(x);
+ var oponentAction = Actions.addPiece(x, goban, !currentPlayer, true, false, false)
+ oponentAction.path = path;
+ path = newPath;
+ actions.push(oponentAction);
+ });
+
+ }
+
+ }
+ currentHistory.push(actions);
history = currentHistory;
+
}
}
@@ -207,8 +278,8 @@ Item {
return repeater.itemAt(x + y * columns)
}
- function getElementAtIndex(index) {
- return repeater.itemAt(index);
+ function itemAt(pos) {
+ return repeater.itemAt(pos);
}
Repeater {
diff --git a/qml/python/game.py b/qml/python/game.py
index de7842a..428fa02 100644
--- a/qml/python/game.py
+++ b/qml/python/game.py
@@ -133,7 +133,7 @@ class Game(object):
""" Create a normalized board, translated on lower coord.
"""
- for transformation in [Translation(self), Rotation(self), Symmetry(self)]:
+ for transformation in [Translation(self), Rotation(self), Symmetry(self), ToIndex(self)]:
if not transformation.is_valid():
continue
diff --git a/qml/python/tests/test2.sgf b/qml/python/tests/test2.sgf
new file mode 100644
index 0000000..292a61f
--- /dev/null
+++ b/qml/python/tests/test2.sgf
@@ -0,0 +1,6 @@
+(;GM[1]FF[3]
+;AW[ok][qk][rl][mm][ln][pn][op][pp][rp][qq][rq][pr][qr][sr]
+AB[qn][qo][ro][np][qp][mq][oq][pq];B[on]
+(;W[po];B[pm];W[no];B[oo])
+(;W[oo];B[no];W[pm];B[po])
+)
diff --git a/qml/python/tests/test_transformations.py b/qml/python/tests/test_transformations.py
index b802b9f..d244e48 100644
--- a/qml/python/tests/test_transformations.py
+++ b/qml/python/tests/test_transformations.py
@@ -3,7 +3,7 @@
import unittest
-from python.transformations import Rotation, Translation, Symmetry
+from python.transformations import Rotation, Translation, Symmetry, ToIndex
class FakeBoard():
@@ -132,3 +132,15 @@ class TestRotation(unittest.TestCase):
symmetry.y_flip = False
self.assertFalse(symmetry.is_valid())
+
+class TestToIndex(unittest.TestCase):
+ """ Test the toIndex transformation.
+ """
+
+ def test_apply_points(self):
+ """ Test the points index.
+ """
+ toIndex = ToIndex(FakeBoard(2, 1, 8, 5))
+ self.assertEqual(0, toIndex.apply_points((2, 1)))
+ self.assertEqual(7, toIndex.apply_points((2, 2)))
+ self.assertEqual(8, toIndex.apply_points((3, 2)))
diff --git a/qml/python/transformations.py b/qml/python/transformations.py
index 3bb9383..40aafc0 100644
--- a/qml/python/transformations.py
+++ b/qml/python/transformations.py
@@ -123,3 +123,32 @@ class Symmetry(object):
"RIGHT": self.board.side["LEFT" if self.x_flip else "RIGHT"],
"BOTTOM":self.board.side["TOP" if self.y_flip else "BOTTOM"]
}
+
+class ToIndex(object):
+ """" Transform each point position in point index.
+ """
+
+ def __init__(self, board):
+ self.board = board
+
+ def is_valid(self):
+ """ This transformation is always valid.
+ """
+ return True;
+
+ def apply_points(self, coord, name = None):
+ """
+ """
+ x_size = min(19, self.board.max_x - self.board.min_x + 1)
+ x, y = coord
+ return (x - self.board.min_x) + (y - self.board.min_y) * x_size
+
+ def get_new_size(self):
+ """ The size is not changed.
+ """
+ return self.board.min_x, self.board.min_y, self.board.max_x, self.board.max_y
+
+ def get_new_side(self):
+ """ There is no changes on the sides.
+ """
+ return self.board.side