Skip to content
This repository has been archived by the owner on Jul 21, 2020. It is now read-only.

Chess #898

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Chess #898

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions extensions/dependencies/chessboard.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<script src="/extensions/widgets/interactive/ChessInput/static/js/chessboard-0.3.0.js"></script>
<script src="/extensions/widgets/interactive/ChessInput/static/js/chess.js"></script>
<link rel="stylesheet" type="text/css" href="/extensions/widgets/interactive/ChessInput/static/css/chessboard-0.3.0.css"></script>
<link rel="stylesheet" type="text/css" href="/extensions/widgets/interactive/ChessInput/static/css/chess.css"></script>
12 changes: 12 additions & 0 deletions extensions/objects/models/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,15 @@ def normalize(cls, raw):
raise TypeError('Cannot convert to graph %s' % raw)

return raw

class ChessPosition(BaseObject):
"""
A chess position, represented by a FEN string.
FEN format is described in http://en.wikipedia.org/wiki/FEN
"""

description = 'A chess position'
edit_html_filename = 'chess_position_editor'
edit_js_filename = 'ChessPositionEditor'

SCHEMA = UnicodeString.SCHEMA
89 changes: 89 additions & 0 deletions extensions/objects/templates/ChessPositionEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
oppia.directive('chessPositionEditor', function($compile, warningsData) {
return {
link: function(scope, element, attrs) {
scope.getTemplateUrl = function() {
return OBJECT_EDITOR_TEMPLATES_URL + scope.$parent.objType;
};
$compile(element.contents())(scope);
},
restrict: 'E',
scope: {},
template: '<span ng-include="getTemplateUrl()"></span>',
controller: ['$scope', '$attrs', function($scope, $attrs) {
$scope.properties = {
whiteKingCastle: true,
whiteQueenCastle: true,
blackKingCastle: true,
blackQueenCastle: true,
toMove: 'b',
} // board state properties

$scope.getSuffix = function() {
var suffix = " "; // rest of the FEN information from scope.properties
p = $scope.properties;
suffix += p.toMove + " "
if (p.whiteKingCastle || p.whiteQueenCastle || // rules for castling
p.blackKingCastle || p.blackQueenCastle) {
if (p.whiteKingCastle) {
suffix += "K";
}
if (p.whiteQueenCastle) {
suffix += "Q";
}
if (p.blackKingCastle) {
suffix += "k";
}
if (p.blackQueenCastle) {
suffix += "q";
}
suffix += " ";
}
else {
suffix += "- ";
}
suffix += "- "; // en passant currently unimplemented
suffix += "0 "; // tracking halfmoves for draw, irrelevant
suffix += "1"; // move number, irrelevant
return suffix;
}

$scope.currentFEN = "" // board FEN position

$scope.$watch( function () {
return $scope.properties // on editing one of the properties
},
function(newValue) {
if (newValue) {
$scope.$parent.value = $scope.currentFEN + $scope.getSuffix();
}
}
);

$scope.onChange = function(oldPos, newPos) {
$scope.currentFEN = ChessBoard.objToFen(newPos);
$scope.$parent.value = $scope.currentFEN + $scope.getSuffix();
};

var boardConfig = {
draggable: true,
position: 'start',
dropOffBoard: 'trash',
sparePieces: true,
onChange: $scope.onChange
};

$scope.alwaysEditable = true;

// make sure the editor partial is loaded, then load chess board js and css
$scope.$watch( function() {
return angular.element('#chess-editor-board').length
},
function(newValue) {
if (newValue)
var board = new ChessBoard('chess-editor-board', boardConfig);
}
);
}]
};
}
);
22 changes: 22 additions & 0 deletions extensions/objects/templates/chess_position_editor.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div>
<div class="row">
<div id="chess-editor-board">
</div>

<div class="chess-settings">
<p>Choose board settings</p>
<select ng-model="properties.toMove">
<option value="w">White to move</option>
<option value="b">Black to move</option>
</select>

<br>

<input type="checkbox" ng-model="properties.whiteKingCastle"> White can castle kingside (0-0) <br>
<input type="checkbox" ng-model="properties.whiteQueenCastle"> White can castle queenside (0-0-0) <br>
<input type="checkbox" ng-model="properties.blackKingCastle"> Black can castle kingside (0-0) <br>
<input type="checkbox" ng-model="properties.blackQueenCastle"> Black can castle queenside (0-0-0)
</div>

</div>
</div>
3 changes: 3 additions & 0 deletions extensions/rules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ class CheckedProofRule(rule_domain.Rule):

class GraphRule(rule_domain.Rule):
subject_type = objects.Graph

class ChessPositionRule(rule_domain.Rule):
subject_type = objects.ChessPosition
42 changes: 42 additions & 0 deletions extensions/rules/chess_position.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# coding: utf-8
#
# Copyright 2014 The Oppia Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, softwar
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Rules for chess positions"""

__author__ = 'Ashvin Nair'

from extensions.rules import base


class Equals(base.ChessPositionRule):
description = 'is equal to {{x|ChessPosition}}'
is_generic = True

def _evaluate(self, subject):
def get_board(fen_string):
return fen_string[:fen_string.index(" ")]

return get_board(subject) == get_board(self.x)

class NotEquals(base.ChessPositionRule):
description = 'is not equal to {{x|ChessPosition}}'
is_generic = True

def _evaluate(self, subject):
def get_board(fen_string):
return fen_string[:fen_string.index(" ")]

return get_board(subject) != get_board(self.x)
14 changes: 14 additions & 0 deletions extensions/widgets/interactive/ChessInput/ChessInput.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script type="text/ng-template" id="interactiveWidget/ChessInput">
<!-- This outer div is needed, otherwise the textarea will overlap the footer of the page. -->
<div class="row">
<div id="input-chess-board-<[$id]>" class="oppia-chess-board">
</div>
</div>
</script>

<script type="text/ng-template" id="response/ChessInput">
<div class="row">
<div id="response-board-<[$id]>" class="oppia-chess-board">
</div>
</div>
</script>
99 changes: 99 additions & 0 deletions extensions/widgets/interactive/ChessInput/ChessInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2014 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* Directive for the ChessInput interactive widget.
*
* IMPORTANT NOTE: The naming convention for customization args that are passed
* into the directive is: the name of the parameter, followed by 'With',
* followed by the name of the arg.
*/

oppia.directive('oppiaInteractiveChessInput', [
'oppiaHtmlEscaper', function(oppiaHtmlEscaper) {
return {
restrict: 'E',
scope: {},
templateUrl: 'interactiveWidget/ChessInput',
controller: ['$scope', '$attrs', function($scope, $attrs) {
// initialize chess object used to check for valid moves
position = oppiaHtmlEscaper.escapedJsonToObj($attrs.chessPositionWithValue)
chess = new Chess(position);
color = position.split(" ")[1];

$scope.onDrop = function(source, target, pc, newPos, oldPos, color) {
move = {from: source, to: target} // todo: deal with promotion
if (chess.move(move)) {
$scope.$parent.$parent.submitAnswer(chess.fen(), 'submit');
}
else {
return 'snapback';
}
};

$scope.onDragStart = function(source, piece, position) {
if (piece[0] != color) {
return False
}
};

var boardConfig = {
draggable: true,
onDrop: $scope.onDrop,
onDragStart: $scope.onDragStart
};

$scope.$watch( function() { // only render board once html is loaded
return angular.element('#input-chess-board-' + $scope.$id).length
}, function(newValue, oldValue) {
if (newValue != 0) {
console.log("position: " + position)
var board = new ChessBoard('input-chess-board-' + $scope.$id, boardConfig);
board.position(position);
}
}
);
}]
};
}
]);


oppia.directive('oppiaResponseChessInput', [
'oppiaHtmlEscaper', function(oppiaHtmlEscaper) {
return {
restrict: 'E',
scope: {},
templateUrl: 'response/ChessInput',
controller: ['$scope', '$attrs', function($scope, $attrs) {
$scope.answer = oppiaHtmlEscaper.escapedJsonToObj($attrs.answer);

var boardConfig = {
draggable: true,
onChange: $scope.onChange
};

$scope.$watch( function() { // only render board once html is loaded
return angular.element('#response-board-' + $scope.$id).length
}, function(newValue, oldValue) {
if (newValue != 0) {
var board = new ChessBoard('response-board-' + $scope.$id, boardConfig);
board.position($scope.answer);
}
}
);
}]
};
}
]);
47 changes: 47 additions & 0 deletions extensions/widgets/interactive/ChessInput/ChessInput.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from core.domain import widget_domain

class ChessInput(widget_domain.BaseWidget):
"""Interactive widget for entering chess positions"""

# The human-readable name of the widget.
name = 'Chess'

# The category the widget falls under in the widget repository.
category = 'Custom'

# A description of the widget.
description = (
'A chess input widget that provides a movable board.'
)

# Customization args and their descriptions, schemas and default
# values.
# NB: There used to be an integer-typed parameter here called 'columns'
# that was removed in revision 628942010573. Some text widgets in
# older explorations may have this customization parameter still set
# in the exploration definition, so, in order to minimize the possibility
# of collisions, do not add a new parameter with this name to this list.
# TODO(sll): Migrate old definitions which still contain the 'columns'
# parameter.
_customization_arg_specs = [{
'name': 'chess_position',
'description': 'Editor for the chess position you want to show the student',
'schema': {
'type': 'custom',
'obj_type': 'ChessPosition',
},
'default_value': 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
}]

# Actions that the reader can perform on this widget which trigger a
# feedback interaction, and the associated input types. Interactive widgets
# must have at least one of these. This attribute name MUST be prefixed by
# '_'.
_handlers = [{
'name': 'submit', 'obj_type': 'ChessPosition'
}]

# Additional JS library dependencies that should be loaded in pages
# containing this widget. These should correspond to names of files in
# feconf.DEPENDENCIES_TEMPLATES_DIR.
_dependency_ids = ['chessboard']
21 changes: 21 additions & 0 deletions extensions/widgets/interactive/ChessInput/static/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Copyright 2013 Chris Oakman
http://chessboardjs.com/

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 changes: 16 additions & 0 deletions extensions/widgets/interactive/ChessInput/static/css/chess.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.oppia-chess-board {
width: 400px;
height: 400px;
margin-left: auto;
margin-right: auto;
}

#chess-editor-board {
width: 400px;
float: left;
}

.chess-settings {
float: right;
margin-right: 10px;
}
Loading