forked from Open-CT/openct-tasks
621 lines
24 KiB
JavaScript
621 lines
24 KiB
JavaScript
function initTask(subTask) {
|
|
var state = {};
|
|
var level;
|
|
var answer = null;
|
|
|
|
/*
|
|
* A configuration is an array of 13 values. Each value is the color of a region, or null for transaprent.
|
|
* The 13 regions cover all possibilities:
|
|
*
|
|
* 2
|
|
* 4 6 12
|
|
* 1 8 0 7 9
|
|
* 10 5 11
|
|
* 3
|
|
*
|
|
*/
|
|
|
|
var colors = {
|
|
orange: "#ff7f27",
|
|
purple: "#803080",
|
|
green: "#00e8a2"
|
|
};
|
|
var symbols = {
|
|
"A": [colors.orange, null, null, null, colors.orange, colors.orange, colors.orange, colors.orange, colors.orange, null, colors.orange, colors.orange, colors.orange],
|
|
"B": [colors.purple, colors.purple, null, null, null, null, null, colors.purple, colors.purple, colors.purple, null, null, null],
|
|
"C": [colors.green, null, colors.green, colors.green, null, colors.green, colors.green, null, null, null, null, null, null]
|
|
};
|
|
var operators = {
|
|
"+": operatorAdd,
|
|
"-": operatorSub,
|
|
"#": operatorCross
|
|
};
|
|
var operatorsButtons = ["+", "-", "#"];
|
|
|
|
var data = {
|
|
easy: {
|
|
target: [null, colors.purple, null, null, null, null, null, null, null, colors.purple, null, null, null],
|
|
parentheses: false
|
|
},
|
|
medium: {
|
|
target: [colors.green, colors.purple, null, null, null, null, null, null, null, colors.purple, null, null, null],
|
|
parentheses: false
|
|
},
|
|
hard: {
|
|
target: [null, null, null, null, null, colors.green, colors.green, colors.purple, colors.purple, null, null, null, null],
|
|
parentheses: true
|
|
}
|
|
};
|
|
|
|
var examples = {
|
|
easy: ["A+C", "A-B", "A#C"],
|
|
medium: ["A+C", "A-B", "A#C", "A-B+C", "A+B#C"],
|
|
hard: ["A+C", "A-B", "A#C", "A-B+C", "A+(B#C)", "A+B#C"]
|
|
};
|
|
|
|
var userVisualConfig;
|
|
var targetVisualConfig;
|
|
var keyboard;
|
|
|
|
var inputLimit = 30;
|
|
|
|
var smallParams = {
|
|
width: 70,
|
|
height: 70,
|
|
scale: 0.27,
|
|
"background-color": "gray"
|
|
};
|
|
|
|
var mediumParams = {
|
|
width: 90,
|
|
height: 100,
|
|
scale: 0.4
|
|
};
|
|
|
|
var bigParams = {
|
|
width: 240,
|
|
height: 240,
|
|
scale: 0.95
|
|
};
|
|
|
|
subTask.loadLevel = function(curLevel) {
|
|
level = curLevel;
|
|
};
|
|
|
|
subTask.getStateObject = function() {
|
|
return state;
|
|
};
|
|
|
|
subTask.reloadAnswerObject = function(answerObj) {
|
|
answer = answerObj;
|
|
if(!answer) {
|
|
return;
|
|
}
|
|
|
|
// Not supposed to happen in regular use.
|
|
if(!isValidPrefix(answer)) {
|
|
answer = "";
|
|
}
|
|
};
|
|
|
|
subTask.resetDisplay = function() {
|
|
initVisualSymbols();
|
|
initExamples();
|
|
initVisualUser();
|
|
initKeyboard();
|
|
updateFromAnswer();
|
|
showError(null);
|
|
};
|
|
|
|
subTask.getAnswerObject = function() {
|
|
return answer;
|
|
};
|
|
|
|
subTask.getDefaultAnswerObject = function() {
|
|
return "";
|
|
};
|
|
|
|
subTask.unloadLevel = function(callback) {
|
|
callback();
|
|
};
|
|
|
|
function createVisualConfig(elementID, visualParams) {
|
|
var paper = subTask.raphaelFactory.create(elementID, elementID, visualParams.width, visualParams.height);
|
|
paper.setStart();
|
|
var array = [
|
|
paper.path("m 365.46715,629.30276 c -9.64827,-0.62577 -21.6938,-2.08094 -29.86147,-3.60751 l -3.72308,-0.69579 -1.23512,-8.76716 c -2.11985,-15.0473 -2.34505,-18.59716 -2.34859,-37.02449 -0.003,-17.2121 0.16916,-20.37798 1.79892,-33.02167 0.81596,-6.32997 2.52959,-16.68752 2.9139,-17.61214 0.25514,-0.61383 1.00999,-0.80571 6.78737,-1.72539 22.37019,-3.56097 42.67938,-4.23506 65.47803,-2.17327 7.77128,0.70277 23.07518,2.97668 23.60417,3.50715 0.27367,0.27445 1.43546,6.58052 2.28778,12.41789 2.32447,15.91979 2.92852,25.75774 2.6731,43.53603 -0.19872,13.83069 -0.5674,18.57689 -2.56906,33.07241 l -1.07325,7.77223 -0.99348,0.17266 c -0.54642,0.095 -3.13142,0.54876 -5.74446,1.00843 -5.93985,1.0448 -13.5335,2.01867 -20.96986,2.68928 -6.72218,0.60621 -30.22026,0.89264 -37.0249,0.45134 z").attr({
|
|
id: 'path4310',
|
|
fill: '#ffff00',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4310'),
|
|
paper.path("m 302.2422,615.99812 c -4.5545,-1.80792 -13.80279,-6.42923 -17.31614,-8.65276 -6.6263,-4.19365 -12.31195,-9.14627 -15.8321,-13.79091 -2.04811,-2.70236 -4.72207,-8.00359 -5.4804,-10.86509 -0.85495,-3.22607 -0.77573,-9.23298 0.16497,-12.50912 3.62131,-12.61182 16.68778,-23.79778 38.09803,-32.61501 2.13503,-0.87926 3.93615,-1.59865 4.00247,-1.59865 0.0663,0 0.12059,18.3052 0.12059,40.67822 0,22.37302 -0.11027,40.67193 -0.24504,40.66426 -0.13478,-0.008 -1.71535,-0.5976 -3.51238,-1.31094 z").attr({
|
|
id: 'path4298',
|
|
fill: '#00ff00',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4298'),
|
|
paper.path("m 341.4942,501.2529 c 1.87387,-5.18976 6.56127,-14.41325 9.97362,-19.6253 8.8004,-13.44179 18.81084,-20.31777 29.57975,-20.31777 10.80122,0 20.76141,6.85982 29.65019,20.42079 3.39663,5.18199 7.89795,14.06099 9.89707,19.52228 l 0.4485,1.22524 -39.99576,0 -39.99577,0 0.4424,-1.22524 z").attr({
|
|
id: 'path4302',
|
|
fill: '#ff00ff',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4302'),
|
|
paper.path("m 377.5526,697.30096 c -15.02183,-2.36717 -29.38427,-19.53857 -38.18687,-45.65532 l -1.23891,-3.67574 42.92605,0 42.92605,0 -0.34685,1.06188 c -4.5228,13.84661 -10.72153,25.98368 -17.51941,34.30291 -4.12956,5.05373 -10.01102,9.82821 -14.61521,11.8644 -4.29001,1.89725 -9.88845,2.74108 -13.94485,2.10187 z").attr({
|
|
id: 'path4304',
|
|
fill: '#ff00ff',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4304'),
|
|
paper.path("m 305.86446,519.17449 0,-16.69965 17.4834,0 c 9.61587,0 17.4834,0.0769 17.4834,0.17092 0,0.094 -0.88606,2.83696 -1.96906,6.09537 -1.083,3.25847 -2.73637,8.99794 -3.67414,12.75446 -0.93778,3.75651 -1.73766,6.8627 -1.77747,6.90264 -0.0399,0.0399 -2.17039,0.54074 -4.73455,1.11284 -5.62288,1.25454 -14.0475,3.57878 -18.79755,5.18603 -1.91336,0.64733 -3.59925,1.17704 -3.74642,1.17704 -0.14723,0 -0.26761,-7.51484 -0.26761,-16.69965 z").attr({
|
|
id: 'path4312',
|
|
fill: '#000000',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4312'),
|
|
paper.path("m 337.02829,645.27422 c -1.7191,-5.58375 -5.08169,-20.28959 -4.72309,-20.65737 0.0739,-0.0761 2.57178,0.29205 5.55098,0.81804 28.07661,4.95761 59.92276,4.89138 87.2017,-0.18135 2.50751,-0.4663 4.63574,-0.76919 4.7295,-0.67309 0.39334,0.40365 -2.85989,14.77559 -4.67986,20.67388 l -0.85828,2.78156 -43.18526,0 -43.18542,0 -0.85027,-2.76167 z").attr({
|
|
id: 'path4322',
|
|
fill: '#ff0000',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4322'),
|
|
paper.path("m 333.50341,527.88068 c -0.0253,-0.99762 3.52858,-13.91335 5.20649,-18.92182 l 2.14955,-6.41625 40.18812,0 40.18812,0 2.16684,6.48568 c 1.93965,5.80564 5.43488,18.71669 5.13763,18.97794 -0.064,0.0563 -2.10125,-0.25014 -4.52725,-0.68086 -13.50125,-2.39707 -26.77521,-3.48302 -42.80198,-3.50166 -15.76709,-0.0184 -27.67107,0.89815 -41.41336,3.1884 -3.45928,0.57652 -6.29165,0.96737 -6.29416,0.86857 z").attr({
|
|
id: 'path4320',
|
|
fill: '#ff0000',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4320'),
|
|
paper.path("m 430.13088,623.28589 c 1.31827,-7.50741 2.54354,-17.48515 3.21987,-26.2203 0.54103,-6.98754 0.544,-28.29479 0.005,-35.45049 -0.76755,-10.18899 -2.54386,-23.48382 -4.15383,-31.08928 l -0.4467,-2.11014 1.03589,0.20818 c 6.13184,1.23231 14.3819,3.41523 21.24593,5.62155 l 4.70195,1.51135 0,40.93249 0,40.93249 -5.59331,1.80106 c -5.39099,1.73591 -18.30426,5.08834 -19.59985,5.08834 -0.54636,0 -0.60147,-0.1628 -0.41492,-1.22525 z").attr({
|
|
id: 'path4308',
|
|
fill: '#808000',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4308'),
|
|
paper.path("m 331.31217,624.58018 c -3.20385,-0.57468 -14.99399,-3.68666 -19.64892,-5.18628 l -5.79815,-1.86793 0.0833,-40.83206 0.0833,-40.83206 3.28316,-1.1107 c 5.31248,-1.79721 12.75197,-3.86823 18.41397,-5.12611 2.90473,-0.64532 5.36138,-1.17331 5.45922,-1.17331 0.0978,0 -0.0319,0.99245 -0.2883,2.20545 -0.90336,4.27352 -2.4002,14.03452 -3.04383,19.84901 -1.17113,10.57979 -1.47211,16.48355 -1.47416,28.91584 -0.003,15.9421 0.70578,25.36942 2.97772,39.62601 0.4519,2.83564 0.77383,5.26691 0.71543,5.40282 -0.0584,0.1359 -0.40169,0.1941 -0.76284,0.12932 z").attr({
|
|
id: 'path4306',
|
|
fill: '#808000',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4306'),
|
|
paper.path("m 455.70599,576.77698 c 0,-22.44572 0.11932,-40.8104 0.26516,-40.8104 0.73333,0 8.35207,3.26117 12.96751,5.5507 8.69101,4.31124 15.32833,8.95767 20.5994,14.42055 4.43553,4.59694 7.06827,8.84297 8.69491,14.02302 0.64829,2.06446 0.76313,3.10983 0.75224,6.84732 -0.012,4.11434 -0.0847,4.61911 -1.08073,7.50803 -0.58734,1.70343 -1.52224,3.90887 -2.07755,4.90099 -5.94086,10.61384 -18.6304,19.87167 -37.4254,27.30423 l -2.69554,1.06596 0,-40.8104 z").attr({
|
|
id: 'path4300',
|
|
fill: '#00ff00',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4300'),
|
|
paper.path("m 305.93046,632.59533 c 0,-8.40336 0.0465,-15.27884 0.10323,-15.27884 0.0568,0 1.43243,0.4606 3.05702,1.02352 4.48944,1.55564 12.97195,3.95223 18.31766,5.17543 l 4.78835,1.09558 0.24327,1.13791 c 1.7352,8.11619 2.8275,12.73536 4.02095,17.00429 l 1.43167,5.12094 -15.98108,0 -15.98107,0 0,-15.27883 z").attr({
|
|
id: 'path4318',
|
|
fill: '#000080',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4318'),
|
|
paper.path("m 425.71482,642.81155 c 1.34917,-4.73644 3.51251,-13.92766 3.96481,-16.84504 l 0.20857,-1.34517 3.64879,-0.76504 c 4.63522,-0.97196 12.06513,-3.01585 17.69325,-4.86731 l 4.37997,-1.44084 0,15.13001 0,15.13 -15.65933,0 -15.65932,0 1.42326,-4.99661 z").attr({
|
|
id: 'path4316',
|
|
fill: '#666666',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4316'),
|
|
paper.path("m 452.15641,534.85806 c -4.52197,-1.60019 -12.3675,-3.81386 -18.0615,-5.09616 -2.89991,-0.65315 -5.34267,-1.2581 -5.42831,-1.34432 -0.0856,-0.0863 -0.41984,-1.33535 -0.74262,-2.77558 -1.18977,-5.30863 -3.09179,-12.13222 -4.85213,-17.40728 -0.9944,-2.97996 -1.80804,-5.50241 -1.80804,-5.60549 0,-0.10307 7.69824,-0.18738 17.1072,-0.18738 l 17.1072,0 0,16.73288 c 0,9.20306 -0.11213,16.72149 -0.24915,16.70765 -0.13702,-0.0135 -1.51971,-0.47481 -3.07265,-1.02432 z").attr({
|
|
id: 'path4314',
|
|
fill: '#00ffff',
|
|
stroke: 'none',
|
|
"stroke-width": '1',
|
|
"stroke-miterlimit": '1.00000012',
|
|
"stroke-dasharray": 'none',
|
|
"stroke-dashoffset": '0',
|
|
"stroke-opacity": '1'
|
|
}).data('id', 'path4314')
|
|
];
|
|
|
|
var set = paper.setFinish();
|
|
|
|
var centerX = visualParams.width / 2;
|
|
var centerY = visualParams.height / 2;
|
|
|
|
// Apply the needed transformations using transformPath (for IE6 compatibility).
|
|
// This brings the shapes' center to the paper's center.
|
|
set.forEach(function(element) {
|
|
element.attr({
|
|
path: Raphael.transformPath(element.attrs.path, ["T", -263 - 118 + centerX, -461 - 118 + centerY])
|
|
});
|
|
});
|
|
|
|
set.transform(["S", visualParams.scale, visualParams.scale, centerX, centerY]);
|
|
set.attr("opacity", 0);
|
|
|
|
return {
|
|
paper: paper,
|
|
set: set,
|
|
array: array
|
|
};
|
|
}
|
|
|
|
function refreshVisualConfig(visualConfig, values) {
|
|
for(var index = 0; index < values.length; index++) {
|
|
var color = values[index];
|
|
var opacity = 1;
|
|
if(values[index] === null) {
|
|
opacity = 0;
|
|
}
|
|
visualConfig.array[index].attr({
|
|
fill: values[index],
|
|
opacity: opacity,
|
|
stroke: color
|
|
});
|
|
}
|
|
}
|
|
|
|
function initVisualSymbols() {
|
|
var symbolsContainerHTML = "";
|
|
var symbol;
|
|
for(symbol in symbols) {
|
|
symbolsContainerHTML += '<div class="symbol sectionBlock" id="symbol_' + symbol + '"><span class="paperTitle">' + symbol + '</span><br><div class="paperDiv symbolButton" id="paperSymbol_' + symbol + '"></div></div>';
|
|
}
|
|
$("#symbolsContainer").html(symbolsContainerHTML);
|
|
function attachClick(symbol) {
|
|
$("#symbol_" + symbol).click(function() { handleKey(symbol.charCodeAt(0)) });
|
|
}
|
|
for(symbol in symbols) {
|
|
attachClick(symbol);
|
|
}
|
|
|
|
for(symbol in symbols) {
|
|
var visualConfig = createVisualConfig("paperSymbol_" + symbol, smallParams);
|
|
refreshVisualConfig(visualConfig, symbols[symbol]);
|
|
}
|
|
}
|
|
|
|
function initExamples() {
|
|
var examplesContainerHTML = '';
|
|
var iExample;
|
|
for(iExample in examples[level]) {
|
|
examplesContainerHTML += '<div class="example sectionBlock"><span class="paperTitle">' + expressionToDisplayString(examples[level][iExample]) + '</span><br><div class="paperDiv" id="paperExample_' + iExample + '"></div></div>';
|
|
}
|
|
$("#examples").html(examplesContainerHTML);
|
|
|
|
for(iExample in examples[level]) {
|
|
var visualConfig = createVisualConfig("paperExample_" + iExample, mediumParams);
|
|
var config = configFromExpression(examples[level][iExample]);
|
|
refreshVisualConfig(visualConfig, config);
|
|
}
|
|
}
|
|
|
|
function initVisualUser() {
|
|
userVisualConfig = createVisualConfig("paperUser", bigParams);
|
|
refreshVisualConfig(userVisualConfig, configFromExpression(answer));
|
|
|
|
targetVisualConfig = createVisualConfig("paperTarget", bigParams);
|
|
refreshVisualConfig(targetVisualConfig, data[level].target);
|
|
}
|
|
|
|
function initKeyboard() {
|
|
keyboard = new Keyboard(handleKey);
|
|
var rows = [];
|
|
/*
|
|
var symbolKeys = [];
|
|
for(var symbol in symbols) {
|
|
symbolKeys.push({keyCode: symbol, buttonText: symbol});
|
|
}
|
|
symbolKeys.sort(keyComparator);
|
|
rows.push(symbolKeys);
|
|
*/
|
|
var operatorKeys = [];
|
|
for(var iOp = 0; iOp < operatorsButtons.length; iOp++) {
|
|
var operator = operatorsButtons[iOp];
|
|
operatorKeys.push({keyCode: operator, buttonText: operator});
|
|
}
|
|
rows.push(operatorKeys);
|
|
|
|
if(data[level].parentheses) {
|
|
rows.push([
|
|
{keyCode: "(", buttonText: "("},
|
|
{keyCode: ")", buttonText: ")"}
|
|
]);
|
|
}
|
|
|
|
rows.push([{
|
|
keyCode: Keyboard.BACKSPACE,
|
|
buttonText: taskStrings.backspace
|
|
}]);
|
|
|
|
keyboard.addMap($("#keyboardContainer"), rows);
|
|
}
|
|
|
|
function keyComparator(key1, key2) {
|
|
return key1.keyCode.charCodeAt(0) - key2.keyCode.charCodeAt(0);
|
|
}
|
|
|
|
function handleKey(key) {
|
|
showError(null);
|
|
|
|
if(key === Keyboard.BACKSPACE) {
|
|
answer = answer.substring(0, answer.length - 1);
|
|
}
|
|
else if(answer.length < inputLimit) {
|
|
var newChar = String.fromCharCode(key);
|
|
var newAnswer = answer + newChar;
|
|
if(isValidPrefix(newAnswer)) {
|
|
answer = newAnswer;
|
|
}
|
|
else {
|
|
if (isSymbol(newChar)) {
|
|
showError(taskStrings.syntaxErrorShape);
|
|
} else {
|
|
showError(taskStrings.syntaxError(newChar));
|
|
}
|
|
}
|
|
}
|
|
|
|
refreshVisualConfig(userVisualConfig, configFromExpression(answer));
|
|
updateFromAnswer();
|
|
}
|
|
|
|
function updateFromAnswer() {
|
|
$("#input").text(expressionToDisplayString(answer));
|
|
}
|
|
|
|
// Escape a string in order to search it using regex.
|
|
// From https://stackoverflow.com/a/3561711
|
|
function escapeRegex(string) {
|
|
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
}
|
|
|
|
function expressionToDisplayString(expression) {
|
|
// Prepend a space to each operator.
|
|
var string = expression;
|
|
for(operator in operators) {
|
|
string = string.replace(new RegExp(escapeRegex(operator), "g"), " " + operator);
|
|
}
|
|
return string;
|
|
}
|
|
|
|
function configFromExpression(expression) {
|
|
|
|
// A config only takes into account the longest prefix which is valid.
|
|
expression = getLongestPrefixValid(expression);
|
|
|
|
/*
|
|
* Auxiliary function:
|
|
* Takes an array without parentheses, in the form:
|
|
* [config, operator, config, operator, ..., config]
|
|
* and returns the resulting config.
|
|
*/
|
|
function evaluateFlat(array) {
|
|
if(array.length === 0) {
|
|
return Beav.Array.make(data[level].target.length, null);
|
|
}
|
|
var result = array[0];
|
|
// Iterate over the operators and apply in order from left to right.
|
|
for(var index = 1; index < array.length; index += 2) {
|
|
var operatorFunc = operators[array[index]];
|
|
result = operatorFunc(result, array[index + 1]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
var currentArray = [];
|
|
var stack = [currentArray];
|
|
for(var index = 0; index < expression.length; index++) {
|
|
var char = expression.charAt(index);
|
|
|
|
// Go higher up the stack.
|
|
if(char === "(") {
|
|
currentArray = [];
|
|
stack.push(currentArray);
|
|
}
|
|
|
|
// Finished an expression in parentheses - evaluate it and push it down.
|
|
else if(char === ")") {
|
|
stack.pop();
|
|
var tempResult = evaluateFlat(currentArray);
|
|
currentArray = stack[stack.length - 1];
|
|
currentArray.push(tempResult);
|
|
}
|
|
|
|
// Symbols are pushed in the current stack level.
|
|
else if(isSymbol(char)) {
|
|
currentArray.push(symbols[char]);
|
|
}
|
|
|
|
// Operators are pushed in the current stack level.
|
|
else {
|
|
currentArray.push(char);
|
|
}
|
|
}
|
|
|
|
// The stack now contains a single level with a flat array.
|
|
return evaluateFlat(stack[0]);
|
|
}
|
|
|
|
// Return the longest prefix of the expression which is itself a valid expression.
|
|
function getLongestPrefixValid(expression) {
|
|
var info = analyzeExpression(expression);
|
|
if(!info.isValidPrefix) {
|
|
return "";
|
|
}
|
|
return expression.substr(0, info.maxValidLength);
|
|
}
|
|
|
|
function isValidPrefix(expression) {
|
|
var info = analyzeExpression(expression);
|
|
return info.isValidPrefix;
|
|
}
|
|
|
|
/*
|
|
* Return an object with the fields:
|
|
* isValidPrefix: boolean indicating whether this expression is a prefix of a valid expression.
|
|
* depth: the number of open brackets that haven't been closed. The expression is valid iff
|
|
* it is a valid prefix, and the depth is 0, and the last character is not an operator.
|
|
* maxValidLength: The length of the longest prefix of this expression which is a valid expression.
|
|
*
|
|
* If the expression is not a valid prefix, the last two field are omitted.
|
|
*/
|
|
function analyzeExpression(expression) {
|
|
if(expression === "") {
|
|
return {
|
|
isValidPrefix: true,
|
|
depth: 0,
|
|
maxValidLength: 0
|
|
};
|
|
}
|
|
|
|
var maxValidLength = 0;
|
|
|
|
// First char must be a symbol or a "(".
|
|
var depth = 0;
|
|
var char;
|
|
// The first character's limitations are equivalent to the situation after an operator.
|
|
var previousChar = "+";
|
|
|
|
for(var index = 0; index < expression.length; index++) {
|
|
char = expression.charAt(index);
|
|
if(char === "(") {
|
|
depth++;
|
|
if(previousChar === ")" || isSymbol(previousChar)) {
|
|
return {isValidPrefix: false};
|
|
}
|
|
}
|
|
else if(char === ")") {
|
|
depth--;
|
|
if(previousChar === "(" || isOperator(previousChar) || depth < 0) {
|
|
return {isValidPrefix: false};
|
|
}
|
|
}
|
|
else if(isSymbol(char)) {
|
|
if(previousChar === ")" || isSymbol(previousChar)) {
|
|
return {isValidPrefix: false};
|
|
}
|
|
}
|
|
else {
|
|
if(previousChar === "(" || isOperator(previousChar)) {
|
|
return {isValidPrefix: false};
|
|
}
|
|
}
|
|
|
|
if(depth === 0 && !isOperator(char)) {
|
|
maxValidLength = index + 1;
|
|
}
|
|
|
|
previousChar = char;
|
|
}
|
|
|
|
return {
|
|
isValidPrefix: true,
|
|
depth: depth,
|
|
maxValidLength: maxValidLength
|
|
};
|
|
}
|
|
|
|
function isOperator(char) {
|
|
return operators[char] !== undefined;
|
|
}
|
|
|
|
function isSymbol(char) {
|
|
return symbols[char] !== undefined;
|
|
}
|
|
|
|
function operatorAdd(config1, config2) {
|
|
return Beav.Array.init(config1.length, function(index) {
|
|
if(config2[index] !== null) {
|
|
return config2[index];
|
|
}
|
|
return config1[index];
|
|
});
|
|
}
|
|
|
|
function operatorSub(config1, config2) {
|
|
return Beav.Array.init(config1.length, function(index) {
|
|
if(config2[index] !== null) {
|
|
return null;
|
|
}
|
|
return config1[index];
|
|
});
|
|
}
|
|
|
|
function operatorCross(config1, config2) {
|
|
return Beav.Array.init(config1.length, function(index) {
|
|
if(config1[index] !== null && config2[index] !== null) {
|
|
return config1[index];
|
|
}
|
|
return null;
|
|
});
|
|
}
|
|
|
|
function showError(string) {
|
|
if(string === null || string === undefined || string === "") {
|
|
string = " ";
|
|
}
|
|
$("#error").html(string);
|
|
}
|
|
|
|
function getResultAndMessage() {
|
|
var userConfig = configFromExpression(answer);
|
|
|
|
for(var index = 0; index < data[level].target.length; index++) {
|
|
if(data[level].target[index] !== userConfig[index]) {
|
|
return {
|
|
successRate: 0,
|
|
message: taskStrings.error
|
|
};
|
|
}
|
|
}
|
|
return {
|
|
successRate: 1,
|
|
message: taskStrings.success
|
|
};
|
|
}
|
|
|
|
subTask.getGrade = function(callback) {
|
|
callback(getResultAndMessage());
|
|
};
|
|
}
|
|
initWrapper(initTask, ["easy", "medium", "hard"]);
|