summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Dailly <sebastien@chimrod.com>2015-02-17 21:49:00 +0100
committerSébastien Dailly <sebastien@chimrod.com>2015-03-19 05:52:45 +0100
commitf07882d76d1221c1a60e016429212abb07fd9db1 (patch)
treeab8089321e6482df58c8e5d44638e1f88872f4ee
parent5049d4c8a05d3f9b72f4c7e048e94b753713beda (diff)
Level navigation tree0.0.1
Load Level collection
-rw-r--r--qml/content/gfx/background.pngbin0 -> 101018 bytes
-rw-r--r--qml/javascript/navigator.js139
-rw-r--r--qml/pages/Board.qml64
-rw-r--r--qml/pages/Goban.qml114
-rw-r--r--qml/pages/collections_list.qml64
-rw-r--r--qml/python/board.py7
-rw-r--r--qml/python/configuration.py25
-rw-r--r--qml/tsumego.qml36
-rw-r--r--translations/tsumego-de.ts25
-rw-r--r--translations/tsumego.ts25
-rw-r--r--tsumego.pro9
11 files changed, 383 insertions, 125 deletions
diff --git a/qml/content/gfx/background.png b/qml/content/gfx/background.png
new file mode 100644
index 0000000..60f57b6
--- /dev/null
+++ b/qml/content/gfx/background.png
Binary files differ
diff --git a/qml/javascript/navigator.js b/qml/javascript/navigator.js
index 984b149..b7e943c 100644
--- a/qml/javascript/navigator.js
+++ b/qml/javascript/navigator.js
@@ -24,10 +24,68 @@ function getCurrentAction(path, tree) {
}
-function checkAction(path, tree, player, playedPosition) {
+/**
+ * return the next move to play in the tree.
+ */
+function getMove(color, tree) {
+
+ var element;
+
+ if (color) {
+ element = tree.W;
+ } else {
+ element = tree.B;
+ }
+
+ if (element !== undefined) {
+ return element[0];
+ }
+ return undefined;
+
+}
+
+/*
+ * return true if the branch is wrong.
+ */
+function isWrong(branch) {
+ return (["WV", "TR"].some(function (element, index, array){
+ return branch.hasOwnProperty(element);
+ }))
+}
+
+function getNextMove(path, tree, player) {
+
+ if (path === undefined) {
+ return false;
+ }
+
+ var way = getCurrentAction(path, tree);
+ if (Array.isArray(way)) {
+ /* get all the possibilities, except the one which are mark wrong */
+ var filtered = way.filter(function callback(element) {
+ return !isWrong(element[0]);
+ });
+ var newIndex = Math.ceil(Math.random() * filtered.length) - 1;
+ return getMove(player, filtered[newIndex][0]);
+
+ } else {
+ return getMove(player, way);
+ }
+}
+
+/**
+ * Compare the player move with the level tree, and check if the player move is allowed.
+ * return undefined if the move was not in the level, or return the new path.
+ */
+function checkAction(path, tree, player, playedPosition, action) {
+
+ if (path === undefined) {
+ return false;
+ }
var way = getCurrentAction(path, tree);
var pathIndex;
+ var properties = {};
if (Array.isArray(way)) {
/*
@@ -43,18 +101,17 @@ function checkAction(path, tree, player, playedPosition) {
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);
- }
+ var expectedIndex = getMove(player, next);
if (playedPosition === expectedIndex) {
+
+ /*
+ * Check for wrong variation.
+ */
+ if (isWrong(next)) {
+ properties.wrong = true;
+ }
+
path.push(0);
return true;
}
@@ -70,62 +127,58 @@ function checkAction(path, tree, player, playedPosition) {
/*
* 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;
+ action(path, properties);
+ return true;
}
-
} 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];
- }
-
+ var move = getMove(player, way);
if (move === playedPosition) {
- console.log("Good !!", path);
+ /*
+ * The player played the good move.
+ */
pathIndex = path.length - 1;
path[pathIndex] = path[pathIndex] + 1;
- return path;
- } else {
- return undefined;
+ action(path, properties);
+ return true;
}
}
+ return false;
}
-function play(path, tree, player, addPiece) {
+/**
+ * Play the computer action.
+ * path: the current path
+ * tree: the level
+ * player: the current player
+ * addPiece: function called with the new move and the new path.
+ */
+function playComputer(path, tree, player, addPiece) {
var way = getCurrentAction(path, tree);
- var pathIndex;
- if (Array.isArray(way)) {
+ var newPath;
+ if (Array.isArray(way)) {
+ /* We have differents branches. We take one at random.
+ */
+ var newIndex = Math.ceil(Math.random() * way.length) - 1;
+ newPath = way[newIndex][0]; /* the player move (index 0) */
+ path.push(newIndex, 1); /* return the oponent move (index 1)*/
} else {
-
- var move;
- if (!player) {
- move = way.W[0];
- } else {
- move = way.B[0];
- }
+ var pathIndex;
pathIndex = path.length - 1;
path[pathIndex] = path[pathIndex] + 1;
- addPiece(move, path);
+ newPath = way;
}
+ var move = getMove(!player, newPath);
+ addPiece(move, path);
}
function undo(path) {
diff --git a/qml/pages/Board.qml b/qml/pages/Board.qml
index 16c4e6b..885fb90 100644
--- a/qml/pages/Board.qml
+++ b/qml/pages/Board.qml
@@ -2,18 +2,17 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
-import io.thp.pyotherside 1.2
-
-Page {
-
- width: Screen.width; height: Screen.height;
+import io.thp.pyotherside 1.3
+Item {
anchors.fill: parent
+ id : board;
+
Column {
- id : column
+ id : column;
anchors.fill: parent;
spacing: 25
@@ -46,6 +45,10 @@ Page {
id:goban
width: parent.width;
height: column.height - (row.height + view.height);
+ onCompletedLevel: {
+ overlay.text = status ? "X" : "✓";
+ overlay.color = status ? "red" : "green" ;
+ }
}
SlideshowView {
@@ -53,25 +56,43 @@ Page {
width: parent.width
height: 200
itemWidth: width / 2
- onCurrentIndexChanged: {py.call('board.getGame', [view.currentIndex], goban.setGoban)}
+ onCurrentIndexChanged: {
+ py.call('board.getGame', [view.currentIndex], goban.setGoban)
+ }
model: 1
delegate: Text {
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
- color: Theme.primaryColor
- font.family: Theme.fontFamily
- font.pixelSize: Theme.fontSizeMedium
+ color: Theme.primaryColor;
+ font.family: Theme.fontFamily;
+ font.pixelSize: Theme.fontSizeMedium;
- width: view.itemWidth
- height: view.height
+ width: view.itemWidth;
+ height: view.height;
text: "Problem " + (index + 1);
}
}
}
+ Text {
+ id: overlay
+ opacity: goban.completed ? 1 : 0
+ anchors {
+ centerIn:parent
+ }
+ font.family: Theme.fontFamily;
+ font.pixelSize: goban.height;
+
+ Behavior on opacity { NumberAnimation { duration: 500 } }
+ }
+
+ function loadBoard(path) {
+ py.loadBoard(path);
+ }
+
Python {
id:py
Component.onCompleted: {
@@ -82,7 +103,7 @@ Page {
importModule('board', function() {
console.log('module loaded');
console.log('Python version: ' + pythonVersion());
- })
+ });
setHandler('log', function (content) {
console.log(content);
@@ -90,12 +111,23 @@ Page {
call('board.setPath', [pythonpath]);
call('board.loadBoard', ["easy.sgf"], function (result) {
- console.log(result + " problems found in the file")
+ console.log(result + " problems found in the file");
view.model = result
call('board.getGame', [0], goban.setGoban);
});
+ }
+ function loadBoard(path) {
+ call('board.loadBoard', [path], function (result) {
+ console.log(result + " problems found in the file");
+ view.model = result
+ call('board.getGame', [0], goban.setGoban);
+ });
}
}
+ function showHint() {
+ goban.showHint();
+ }
+
}
diff --git a/qml/pages/Goban.qml b/qml/pages/Goban.qml
index 66002f7..11e6328 100644
--- a/qml/pages/Goban.qml
+++ b/qml/pages/Goban.qml
@@ -32,6 +32,11 @@ Item {
property bool freePlay: false;
+ /*
+ * flag to tell if player is on a wrong branch.
+ */
+ property bool isWrong: false;
+
/**
* The game tree.
*/
@@ -47,13 +52,16 @@ Item {
*/
property variant history;
+ signal completedLevel(bool status);
+ signal startup();
+
/*
- * Start the game.
- *
- * Initialize the board with the stones, and set player color.
+ * Start the game. Initialize the board with the stones, and set player color.
+ * Function called at first launch, or whene resetting the game.
*/
function start() {
+ startup();
completed = false;
for (var i = 0; i < goban.rows * goban.columns; i++) {
@@ -86,6 +94,7 @@ Item {
goban.itemAt(pos).put(false, false);
});
}
+ isWrong = false;
}
function setGoban(ret) {
@@ -109,7 +118,6 @@ Item {
}
initialPlayer = (ret.current_player === 'W');
- console.log(ret.current_player);
/*
* Put the initials stones
@@ -118,6 +126,16 @@ Item {
start();
}
+ function showHint() {
+
+ if (path === undefined) {
+ console.log("no hint to show");
+ return;
+ }
+ var action = TreeNavigator.getNextMove(path, tree, currentPlayer);
+ clickHandler(action);
+ }
+
/*
* Undo the last move.
*/
@@ -126,6 +144,7 @@ Item {
return;
}
+ completed = false;
var currentHistory = history;
var actions = currentHistory.pop();
@@ -135,9 +154,13 @@ Item {
Actions.undo(goban, x);
currentPlayer = x.player;
path = x.path;
+ if (x.wrong !== undefined) {
+ isWrong = false;
+ }
});
history = currentHistory;
+
}
/**
@@ -145,10 +168,6 @@ 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))
@@ -158,56 +177,65 @@ Item {
}
var step = Actions.addPiece(index, goban, currentPlayer, true, false, false);
+ if (step === undefined) {
+ /* Movement not allowed. */
+ return;
+ }
- if (step !== undefined) {
-
+ if (completed) {
+ completed = false;
+ }
- /*
- * Update the path.
- */
- var currentPosition = path[path.length - 1];
- step.path = path;
- var action = TreeNavigator.checkAction(path, tree, currentPlayer, index);
- path = action;
+ /*
+ * Update the path.
+ */
+ step.path = path;
+ /*
+ * Update the history with the last move.
+ */
+ var currentHistory = history;
+ var actions = [step];
- /*
- * Update the history with the last move.
- */
- var currentHistory = history;
- var actions = [step];
+ var followLevel = TreeNavigator.checkAction(path, tree, currentPlayer, index, function (newPath, properties) {
+ if (properties.wrong !== undefined) {
+ isWrong = true;
+ step.wrong = true;
+ }
- if (action === undefined) {
- /*
- * Switch to variation mode
- */
+ if (TreeNavigator.getCurrentAction(newPath, tree) === undefined) {
+ completed = true;
+ completedLevel(isWrong);
} else {
- if (TreeNavigator.getCurrentAction(action, tree) === undefined) {
- console.log("Level completed!");
- completed = true;
- return;
- } else {
+ /* Play the openent move. */
+ path = newPath;
+ TreeNavigator.playComputer(newPath, tree, currentPlayer, function(x, newPath) {
+ var oponentAction = Actions.addPiece(x, goban, !currentPlayer, true, false, false)
+ oponentAction.path = path;
+ path = newPath;
+ actions.push(oponentAction);
+ });
+ if (TreeNavigator.getCurrentAction(path, tree) === undefined) {
/*
- * Play the openent move.
+ * Level has been completed by the computer 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);
- });
-
+ completed = true;
+ currentPlayer = !currentPlayer;
+ completedLevel(isWrong);
}
}
- currentHistory.push(actions);
- history = currentHistory;
+ });
+ if (!followLevel || completed) {
+ path = undefined;
+ currentPlayer = !currentPlayer;
}
+ currentHistory.push(actions);
+ history = currentHistory;
+
}
/**
diff --git a/qml/pages/collections_list.qml b/qml/pages/collections_list.qml
new file mode 100644
index 0000000..6701869
--- /dev/null
+++ b/qml/pages/collections_list.qml
@@ -0,0 +1,64 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+
+import io.thp.pyotherside 1.3
+
+Page {
+ width: Screen.width; height: Screen.height;
+
+ signal openCollection(string path);
+
+ Column {
+ Text {
+ id: txt;
+ text: qsTr("Select the collection to open");
+
+ }
+
+ SilicaListView {
+
+ width: parent.width; height: Screen.height;
+
+ model: ListModel {id: levelsList}
+
+ delegate: ListItem {
+ Label {
+ text: name
+ }
+ onClicked: {
+ openCollection(path);
+ pageStack.pop()
+ }
+
+ }
+ }
+
+ }
+
+
+ Python {
+ id:py
+ Component.onCompleted: {
+ var pythonpath = Qt.resolvedUrl('../python').substr('file://'.length);
+ addImportPath(pythonpath);
+ console.log(pythonpath);
+
+ importModule('configuration', function() {
+ console.log('module loaded');
+ console.log('Python version: ' + pythonVersion());
+ });
+
+ setHandler('log', function (content) {
+ console.log(content);
+ });
+
+ call('configuration.get_levels', [pythonpath, StandardPaths.documents], function(result) {
+ result.forEach(function(elem) {
+ console.log(elem);
+ levelsList.append(elem);
+ })
+ });
+ }
+ }
+
+}
diff --git a/qml/python/board.py b/qml/python/board.py
index 5bc0ac5..19f720a 100644
--- a/qml/python/board.py
+++ b/qml/python/board.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import os
+import os.path
try:
import pyotherside
except:
@@ -22,7 +23,10 @@ def setPath(qtPath):
def loadBoard(filename):
global cursor
- sgfPath = os.path.join(path,"../content","sgf",filename);
+ if os.path.isfile(filename):
+ sgfPath = filename
+ else:
+ sgfPath = os.path.join(path,"../content","sgf",filename);
pyotherside.send('log', sgfPath)
try:
f = open(sgfPath)
@@ -48,7 +52,6 @@ def getGame(n):
game = Game(cursor)
game.normalize()
- pyotherside.send('log', "Game loaded !!")
return {
"tree": game.tree,
diff --git a/qml/python/configuration.py b/qml/python/configuration.py
new file mode 100644
index 0000000..e2cd826
--- /dev/null
+++ b/qml/python/configuration.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+import os.path
+
+try:
+ import pyotherside
+except:
+ print("no pyotherside module loaded")
+
+def get_level_desc(path):
+ return {"name": os.path.basename(path),
+ "path": path,
+ }
+
+def get_levels(qtPath, documents):
+
+ pyotherside.send('log', documents)
+
+ level_path = os.path.join(qtPath, "../content", "sgf")
+ provided_levels = [get_level_desc(os.path.join(level_path, f)) for f in os.listdir(level_path)]
+
+ return provided_levels
+
diff --git a/qml/tsumego.qml b/qml/tsumego.qml
index 9ead16a..f86ed24 100644
--- a/qml/tsumego.qml
+++ b/qml/tsumego.qml
@@ -30,19 +30,41 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
-import "pages"
+import "pages/"
ApplicationWindow
{
+ initialPage: Page {
+ SilicaFlickable {
- initialPage: Component {
+ anchors.fill: parent
- id:app
+ PullDownMenu {
+ MenuItem {
+ text: qsTr("Options")
+ onClicked: {}
+ }
+ MenuItem {
+ text: qsTr("Load level")
+ onClicked: {
+ var options = pageStack.push(Qt.resolvedUrl("pages/collections_list.qml"));
+ options.openCollection.connect(function(path) {
+ board.loadBoard(path);
+ });
+ }
+ }
+ MenuItem {
+ text: qsTr("Hint")
+ onClicked: {
+ board.showHint();
+ }
+ }
+ }
+ Board {id:board}
- Board {id:board}
- }
-
- cover: Qt.resolvedUrl("cover/CoverPage.qml")
+ }
+ }
}
+
diff --git a/translations/tsumego-de.ts b/translations/tsumego-de.ts
index ba4e6f9..a2fc0f4 100644
--- a/translations/tsumego-de.ts
+++ b/translations/tsumego-de.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.0">
+<TS version="2.1">
<context>
<name>CoverPage</name>
<message>
@@ -28,15 +28,28 @@
</message>
</context>
<context>
- <name>SecondPage</name>
+ <name>collections_list</name>
<message>
- <location filename="../qml/pages/SecondPage.qml" line="42"/>
- <source>Nested Page</source>
+ <location filename="../qml/pages/collections_list.qml" line="14"/>
+ <source>Select the collection to open</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>tsumego</name>
+ <message>
+ <location filename="../qml/tsumego.qml" line="44"/>
+ <source>Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/tsumego.qml" line="48"/>
+ <source>Load level</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/SecondPage.qml" line="49"/>
- <source>Item</source>
+ <location filename="../qml/tsumego.qml" line="57"/>
+ <source>Hint</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/translations/tsumego.ts b/translations/tsumego.ts
index ba4e6f9..a2fc0f4 100644
--- a/translations/tsumego.ts
+++ b/translations/tsumego.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.0">
+<TS version="2.1">
<context>
<name>CoverPage</name>
<message>
@@ -28,15 +28,28 @@
</message>
</context>
<context>
- <name>SecondPage</name>
+ <name>collections_list</name>
<message>
- <location filename="../qml/pages/SecondPage.qml" line="42"/>
- <source>Nested Page</source>
+ <location filename="../qml/pages/collections_list.qml" line="14"/>
+ <source>Select the collection to open</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>tsumego</name>
+ <message>
+ <location filename="../qml/tsumego.qml" line="44"/>
+ <source>Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/tsumego.qml" line="48"/>
+ <source>Load level</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/SecondPage.qml" line="49"/>
- <source>Item</source>
+ <location filename="../qml/tsumego.qml" line="57"/>
+ <source>Hint</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tsumego.pro b/tsumego.pro
index e063572..254c69c 100644
--- a/tsumego.pro
+++ b/tsumego.pro
@@ -18,7 +18,6 @@ SOURCES += src/tsumego.cpp
OTHER_FILES += qml/tsumego.qml \
qml/cover/CoverPage.qml \
- qml/pages/FirstPage.qml \
qml/content/gfx/*.png \
rpm/tsumego.changes.in \
rpm/tsumego.spec \
@@ -38,7 +37,13 @@ OTHER_FILES += qml/tsumego.qml \
qml/python/game.py \
qml/pages/Goban.qml \
qml/content/sgf/hard.sgf \
- qml/content/sgf/easy.sgf
+ qml/content/sgf/easy.sgf \
+ qml/javascript/actions.js \
+ qml/javascript/goban_util.js \
+ qml/javascript/navigator.js \
+ qml/content/gfx/ok.svg \
+ qml/pages/collections_list.qml \
+ qml/python/configuration.py
# to disable building translations every time, comment out the
# following CONFIG line