openct-tasks/bebras/2016/2016-JP-01-potions/task.js

1433 lines
49 KiB
JavaScript

function initTask(subTask) {
var state = null;
var level;
var answer = null;
var data = {
easy: {
height1: 180,
totalCups: 4,
experimentCups: 2,
beavers: 3,
experiments: [
["A", "B"],
["B", "C"],
["C", "D"]
],
showText: true
},
medium: {
height1: 140,
totalCups: 6,
experimentCups: 3,
beavers: 3,
experiments: [
["A", "B", "C"],
["A", "D", "E"],
["C", "D", "F"]
]
},
hard: {
height1: 220,
totalCups: 8,
experimentCups: 4,
beavers: 3
}
};
var allProperties = [
"curlyWhiskers",
"bigTeeth",
"bigEars",
"blackTeeth",
"blackEars",
"whiteSkin",
"whiteNose",
"whiteEyes"
];
var levelProperties;
var cupToLabel = ["A", "B", "C", "D", "E", "F", "G", "H"];
var labelToCup = {
A: 0,
B: 1,
C: 2,
D: 3,
E: 4,
F: 5,
G: 6,
H: 7
};
var cupToProperty;
var paper1;
var paper2;
var experimentBeavers;
var dragAndDrop1;
var dragAndDrop2;
var drinkButton;
var reshuffleButton;
var randomGenerator;
var internalSeed = null;
var wrongHighlighter;
var dragContainers = {
sourceCups: null,
beaverCups: null,
sourceBeavers: null,
answerSlots: null
};
var paperParams = {
width: 760,
height2: 360
};
var experimentParams = {
firstCenterX: 240,
beaverXPad: 140,
beaverCenterY: {
easy: 100,
medium: 100,
hard: 170
},
beaverTopPad: 70,
fixedBeaverX: 80,
cupXPad: 2,
cupSlotWidth: 30,
cupSlotHeight: 50,
cupSlotAttr: {
fill: "white"
},
cupSlotSpacing: 20,
cupSourceY: 30,
drinkButton: {
width: 154,
height: 56,
x: 600
},
reshuffleButton: {
width: 154,
height: 56,
x: 600
}
};
var beaverParams = {
bottomPad: 58,
textYJump: 24,
textYJumpSqueezed: 10,
propertyAttr: {
"font-size": 18
},
width: 106, // For calculations. Actual size is in drawBeaver.
height: 95,
scale: 0.8
};
var beaverPNGParams = {
width: 85,
height: 79
};
var cupParams = {
labelAttr: {
"font-size": 22,
"font-weight": "bold"
},
labelYOffset: 4,
width: 28, // For calculations. Actual size is in drawCup paths.
height: 45
};
var answerSlotParams = {
width: 90,
height: 145,
centerY: 140,
backgroundAttr: {
"stroke-width": 2
},
cupYOffset: -110,
bottomPad: 120
};
var highlightParams = {
attr: {
stroke: "red",
"stroke-width": 4
}
};
subTask.loadLevel = function(curLevel, curState) {
level = curLevel;
state = curState;
displayHelper.hideValidateButton = true;
initProperties();
if(internalSeed === null) {
internalSeed = subTask.taskParams.randomSeed;
randomGenerator = new RandomGenerator(internalSeed);
}
};
subTask.getStateObject = function() {
return state;
};
subTask.reloadAnswerObject = function(answerObj) {
answer = answerObj;
if(answer) {
randomizePotions();
}
};
subTask.resetDisplay = function() {
initPaper();
initTexts();
drawFixedBeaver();
initAnswer();
if(data[level].experiments) {
drawDataExperiments();
}
else {
initUserExperiments();
if(answer.state == "after") {
performUserExperiments();
}
}
showFeedback(null);
unhighlightWrong();
initButtons();
};
subTask.getAnswerObject = function() {
return answer;
};
subTask.getDefaultAnswerObject = function() {
var answerObject = {
seed: randomGenerator.nextInt(0, 100000),
cupToIndex: Beav.Array.make(data[level].totalCups, null),
state: "irrelevant",
safeShuffling: true // This flag is to distinguish the participants' answers before and after the safe shuffling fix.
};
if(!data[level].experiments) {
answerObject.experiments = Beav.Matrix.make(data[level].beavers, data[level].experimentCups, null);
answerObject.state = "before";
}
return answerObject;
};
subTask.getDefaultStateObject = function() {
return {};
};
subTask.unloadLevel = function(callback) {
if(drinkButton) {
drinkButton.remove();
drinkButton = null;
}
if(reshuffleButton) {
reshuffleButton.remove();
reshuffleButton = null;
}
if(dragAndDrop1) {
dragAndDrop1.disable();
}
if(dragAndDrop2) {
dragAndDrop2.disable();
}
callback();
};
function initButtons() {
$("#execute").unbind("click");
$("#execute").click(clickValidate);
}
function initProperties() {
// Pick the properties for this level (once per level - doesn't change across answer seeds).
var tempGenerator = new RandomGenerator(subTask.taskParams.randomSeed);
levelProperties = $.extend(true, [], allProperties);
levelProperties = levelProperties.slice(0, data[level].totalCups);
tempGenerator.shuffle(levelProperties);
}
function randomizePotions() {
randomGenerator.reset(answer.seed);
cupToProperty = $.extend(true, [], levelProperties);
// We need safe shuffling so that the order of the answer beavers
// doesn't resemble the answer too much.
if(answer.safeShuffling) {
randomGenerator.safeShuffle(cupToProperty);
}
else {
randomGenerator.shuffle(cupToProperty);
}
}
function initPaper() {
paper1 = subTask.raphaelFactory.create("anim1", "anim1", paperParams.width, data[level].height1);
paper2 = subTask.raphaelFactory.create("anim2", "anim2", paperParams.width, paperParams.height2);
dragAndDrop1 = DragAndDropSystem({
paper: paper1,
actionIfDropped: actionIfDropped1,
drop: onDrop,
canBeTaken: canBeTaken1
});
dragAndDrop2 = DragAndDropSystem({
paper: paper2,
actionIfDropped: actionIfDropped2,
drop: onDrop,
actionIfEjected: actionIfEjected2,
canBeTaken: canBeTaken2
});
}
function initTexts() {
$(".totalCups").html(data[level].totalCups);
$(".experimentCups").html(data[level].experimentCups);
}
function drawFixedBeaver() {
paper1.text(experimentParams.fixedBeaverX, experimentParams.beaverCenterY[level] - experimentParams.beaverTopPad, taskStrings.noDrink).attr("font-size", 16);
drawBeaver(paper1, experimentParams.fixedBeaverX, experimentParams.beaverCenterY[level], false);
}
function drawCup(paper, centerX, centerY, label) {
var transform = ["t", -396.89313 - 20 - 14 + centerX, -512.87486 - 20 - 22 + centerY];
paper.setStart();
paper.path(Raphael.transformPath("m 417.83263,537.22917 c 0,0 1.56065,25.47683 2.72137,31.89977 1.16072,6.42295 1.69067,7.78504 10.37,7.6968 4.75521,-0.0483 9.95597,0.21638 10.72067,-7.01808 l 3.45535,-32.68929", transform)).attr({
id: 'path5105',
parent: 'layer1',
fill: 'white',
"fill-rule": 'evenodd',
stroke: '#000000',
"stroke-width": '1.47',
"stroke-linecap": 'butt',
"stroke-linejoin": 'miter',
"stroke-opacity": '1'
});
paper.ellipse(431.40475 + transform[1], 537.31726 + transform[2], 13.705804, 3.6365776).attr({
id: 'path5107',
parent: 'layer1',
opacity: '1',
fill: '#77f442',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.61',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-dashoffset": '0',
"stroke-opacity": '1'
});
paper.text(centerX, centerY + cupParams.labelYOffset, label).attr(cupParams.labelAttr);
var overlay = paper.rect(
centerX - cupParams.width / 2,
centerY - cupParams.height / 2,
cupParams.width,
cupParams.height
);
overlay.attr({
fill: "green",
opacity: 0
});
return paper.setFinish();
}
function drawBeaver(paper, centerX, centerY, properties, writeText, squeezeText, loadImage) {
if(!properties) {
properties = {};
}
// Constants are SVG artifacts.
var transform1 = ["t", centerX - 20 - 53, -918.12717 - 30 - 38 + centerY, "S", beaverParams.scale, beaverParams.scale, centerX, centerY];
var transform2 = ["m", 1.0150258, 0, 0, 0.74760486, 0, 0, "T" ,-493 - 53 + centerX, - 210 - 16 + centerY, "S", beaverParams.scale, beaverParams.scale, centerX, centerY];
paper.setStart();
if(loadImage) {
var singleProperty;
for(var _property in properties) {
singleProperty = _property;
}
var imageName = $("#" + singleProperty + "_image").attr("src");
paper.image(imageName, centerX - beaverPNGParams.width / 2, centerY - beaverPNGParams.height / 2, beaverPNGParams.width, beaverPNGParams.height);
}
else {
/***************
* Ears
***************/
var leftEar, rightEar;
if (properties.bigEars) {
var bigEarScaleX = 1.05;
var bigEarScaleY = 1.05;
// Big right ear.
rightEar = paper.path(
Raphael.transformPath(
Raphael.transformPath("m 83.829933,958.67743 c 0,0 11.007933,-32.88261 20.214677,-13.91954 2.61603,5.3882 -11.177504,17.2872 -11.177504,17.2872 z", transform1),
["S", bigEarScaleX, bigEarScaleY, centerX, centerY]
)).attr({
id: 'path3874-0-29-1',
parent: 'layer1',
fill: '#e58f56',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.49',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Big left ear.
leftEar = paper.path(
Raphael.transformPath(
Raphael.transformPath("m 60.090893,961.44278 c 0,0 -12.47312,-33.51026 -22.90529,-14.18524 -2.964215,5.49106 12.66528,17.61715 12.66528,17.61715 z", transform1),
["S", bigEarScaleX, bigEarScaleY, centerX, centerY]
)).attr({
id: 'path3874-7-7-7-0',
parent: 'layer1',
fill: '#e58f56',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.49',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
} else {
// Normal right ear.
rightEar = paper.path(Raphael.transformPath("m 86.280666,960.64094 c 0,0 6.36777,-18.79121 11.6936,-7.95451 1.5133,3.07916 -6.46586,9.879 -6.46586,9.879 z", transform1)).attr({
id: 'path3874-0-29-4',
parent: 'layer1',
fill: '#e58f56',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.41',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Normal left ear.
leftEar = paper.path(Raphael.transformPath("m 57.706166,960.60394 c 0,0 -6.77158,-18.40586 -12.43514,-7.79139 -1.60926,3.01602 6.8759,9.6764 6.8759,9.6764 z", transform1)).attr({
id: 'path3874-0-29-4',
parent: 'layer1',
fill: '#e58f56',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.41',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
}
if(properties.blackEars) {
leftEar.attr("fill", "black");
rightEar.attr("fill", "black");
}
/***************
* Skin
***************/
var skin = paper.path(Raphael.transformPath("m 44.199853,984.31305 c 0,0 -6.46966,-27.32056 25.39523,-28.56374 22.02416,-0.85925 33.048767,6.31221 29.268463,30.08454 0,0 21.208654,29.43315 -10.794543,37.10065 0,0 -15.61102,3.2184 -33.47555,0 0,0 -29.286385,-5.9847 -10.3936,-38.62145 z", transform1)).attr({
id: 'path3801-6-2-2',
parent: 'layer1',
fill: '#e58f56',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.41',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
if(properties.whiteSkin) {
skin.attr("fill", "white");
}
/***************
* Teeth
***************/
var teethInline, teethOutline;
if(properties.bigTeeth) {
// Big teeth outline
teethOutline = paper.path(Raphael.transformPath("m 57.649003,1002.6017 c 0,0 1.67017,26.7616 2.50523,28.0064 0.8351,1.2447 21.30903,1.5559 22.35288,-0.3112 1.04385,-1.8671 2.0877,-28.0064 2.0877,-28.0064", transform1)).attr({
id: 'path3830-7-4-8',
parent: 'layer1',
display: 'inline',
fill: '#ffffff',
stroke: '#000000',
"stroke-width": '1.49',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Big teeth inline.
teethInline = paper.path(Raphael.transformPath("m 70.586823,1030.5349 c 0,0 0,-30.4769 0.34544,-30.4769", transform1)).attr({
id: 'path3850-83-6-6-1',
parent: 'layer1',
display: 'inline',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.49',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
} else {
// Teeth outline.
teethOutline = paper.path(Raphael.transformPath("m 57.429856,1001.2609 c 0,0 1.6974,14.9676 2.54609,15.6638 0.84872,0.6961 21.65661,0.8702 22.71748,-0.1741 1.06088,-1.0442 2.12176,-15.6637 2.12176,-15.6637", transform1)).attr({
id: 'path3830-7-4-5',
parent: 'layer1',
display: 'inline',
fill: '#ffffff',
stroke: '#000000',
"stroke-width": '1.35',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Teeth inline.
teethInline = paper.path(Raphael.transformPath("m 70.578706,1016.8837 c 0,0 0,-17.04547 0.35108,-17.04547", transform1)).attr({
id: 'path3850-83-6-6-5',
parent: 'layer1',
display: 'inline',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.35',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
}
if(properties.blackTeeth) {
teethOutline.attr("fill", "black");
teethInline.attr("stroke", "white");
}
/***************
* Eyes
***************/
var leftEye, rightEye;
// Left eye.
leftEye = paper.path(Raphael.transformPath("m 62.378043,979.43698 c 0,0 3.5782,0.83497 3.82637,-3.49829 0.20605,-3.59778 -0.0463,-6.15326 -3.50967,-5.92238 -3.27189,0.21813 -2.57377,5.40741 -2.57377,5.40741 0,0 0.38526,4.01326 2.25707,4.01326 z", transform1)).attr({
id: 'path3853-64-3-2',
parent: 'layer1',
display: 'inline',
fill: '#000000',
stroke: '#000000',
"stroke-width": '0.98',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Right eye.
rightEye = paper.path(Raphael.transformPath("m 79.386193,970.37636 c 0,0 3.86283,-1.81492 4.11163,3.41344 0.16525,3.47214 -0.20413,5.85902 -3.51885,5.71218 -3.28423,-0.14542 -2.58047,-5.21547 -2.58047,-5.21547 0,0 0.22893,-3.24163 1.98769,-3.91015 z", transform1)).attr({
id: 'path3853-1-4-8-7',
parent: 'layer1',
display: 'inline',
fill: '#000000',
stroke: '#000000',
"stroke-width": '0.96',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
if(properties.whiteEyes) {
leftEye.attr("fill", "white");
leftEye.attr("stroke-width", 2);
rightEye.attr("fill", "white");
rightEye.attr("stroke-width", 2);
}
/***************
* Cheeks
***************/
// Bottom left cheek.
paper.path(Raphael.transformPath("m 71.011363,1000.8735 c 0,0 -20.89069,16.405 -26.91437,-2.81955", transform1)).attr({
id: 'path3812-1-1',
parent: 'layer1',
display: 'inline',
fill: '#ffad76',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.41',
"stroke-linecap": 'square',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Top left cheek.
paper.path(Raphael.transformPath("m 44.632843,998.33775 c 0,0 -0.76897,-10.5095 8.20248,-9.9968 0,0 10.50942,-0.5333 14.61064,6.3876 4.3908,7.40945 3.84492,6.17235 3.84492,6.17235", transform1)).attr({
id: 'path3977-2-5-8-1',
parent: 'layer1',
fill: '#ffad76',
"fill-opacity": '1',
stroke: 'none',
'stroke-width': '1',
'stroke-opacity': '1'
});
// Bottom right cheek.
paper.path(Raphael.transformPath("m 71.267703,1000.8735 c 0,0 20.890673,16.405 26.914353,-2.81955", transform1)).attr({
id: 'path3827-9-9-5',
parent: 'layer1',
display: 'inline',
fill: '#ffad76',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.41',
"stroke-linecap": 'square',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Top right cheek.
paper.path(Raphael.transformPath("m 97.287486,998.72215 c 0,0 3.844924,-11.022 -11.022073,-9.4841 0,0 -10.25308,3.0554 -11.79104,5.875 -4.1242,7.56105 -3.84492,6.17235 -3.84492,6.17235", transform1)).attr({
id: 'path3977-2-7-5-9-9',
parent: 'layer1',
fill: '#ffad76',
"fill-opacity": '1',
stroke: 'none',
'stroke-width': '1',
'stroke-opacity': '1'
});
/***************
* Whiskers
***************/
if(properties.curlyWhiskers) {
// Right whiskers.
paper.path(Raphael.transformPath("m 89.634026,985.56156 c 0,0 13.215534,-3.73847 27.559214,-3.45142 3.57094,0.0715 5.8498,0.11582 6.48581,-3.21999 0.59976,-3.14569 -2.08057,-4.09658 -3.74654,-3.98206 -1.72852,0.11883 -3.77252,2.13975 -1.98145,4.13821", transform1)).attr({
id: 'path3895-7-79-6',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 90.015356,989.51547 c 0,0 12.930294,-1.42769 26.260384,1.32369 3.31859,0.68499 4.57594,1.77731 5.7096,-1.41554 1.06906,-3.01087 -0.48027,-4.27604 -2.05247,-4.44923 -1.63124,-0.17965 -3.42382,1.47522 -2.07733,3.76165", transform1)).attr({
id: 'path3895-7-79-6-1',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.24',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 89.407216,994.03229 c 0,0 13.283954,1.01867 26.257994,6.21531 3.22994,1.2938 5.07123,2.7426 6.75938,-0.18 1.59191,-2.7561 -0.44613,-4.82181 -2.00216,-5.28615 -1.61445,-0.48177 -4.01627,0.34451 -3.04849,2.84144", transform1)).attr({
id: 'path3895-7-79-6-2',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.25',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Left whiskers.
paper.path(Raphael.transformPath("m 54.895696,985.33855 c 0,0 -13.21553,-3.73847 -27.55921,-3.45142 -3.57094,0.0715 -5.8498,0.11582 -6.48581,-3.21999 -0.59976,-3.14569 2.08057,-4.09658 3.74654,-3.98206 1.72852,0.11883 3.77252,2.13975 1.98145,4.13821", transform1)).attr({
id: 'path3895-7-79-6-6',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 54.514366,989.29246 c 0,0 -12.93029,-1.42769 -26.26038,1.32369 -3.31859,0.68499 -4.57594,1.77731 -5.7096,-1.41554 -1.06906,-3.01087 0.48027,-4.27604 2.05247,-4.44923 1.63124,-0.17965 3.42382,1.47522 2.07733,3.76165", transform1)).attr({
id: 'path3895-7-79-6-1-9',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.24',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 55.122506,993.80928 c 0,0 -13.28395,1.01867 -26.25799,6.21532 -3.22994,1.2938 -5.07123,2.7426 -6.75938,-0.18006 -1.59191,-2.75605 0.44613,-4.82176 2.00216,-5.2861 1.61445,-0.48177 4.01627,0.34451 3.04849,2.84144", transform1)).attr({
id: 'path3895-7-79-6-2-2',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.25',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
} else {
// Left whiskers.
paper.path(Raphael.transformPath("m 53.196623,984.53185 c 0,0 -16.91762,-3.8449 -32.553565,-3.5886", transform1)).attr({
id: 'path3895-4-3-3-2',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'miter',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 52.940273,990.17105 c 0,0 -30.502925,-1.2816 -31.528225,-0.769", transform1)).attr({
id: 'path3897-0-4-0-3',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'miter',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 53.452953,994.78495 -31.528255,3.5886", transform1)).attr({
id: 'path3899-9-0-4-7',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'miter',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Right whiskers.
paper.path(Raphael.transformPath("m 89.633423,985.56195 c 0,0 18.220107,-3.8367 35.059867,-3.5809", transform1)).attr({
id: 'path3895-7-79-9',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 89.909493,991.18915 c 0,0 32.851377,-1.2789 33.955627,-0.7674", transform1)).attr({
id: 'path3897-4-8-5',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
paper.path(Raphael.transformPath("m 89.357363,995.79315 33.955627,3.5809", transform1)).attr({
id: 'path3899-7-6-9',
parent: 'layer1',
fill: 'none',
stroke: '#000000',
"stroke-width": '1.27',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
}
/***************
* Nose
***************/
var leftNose, rightNose;
// Left nose.
leftNose = paper.path(Raphael.transformPath("m 533.36054,323.53712 c 0,0 -19.44544,-21.1318 -0.50507,-21.2132", transform2)).attr({
id: 'path3804-8-0-8',
parent: 'layer1',
fill: '#000000',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.54',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
// Right nose.
rightNose = paper.path(Raphael.transformPath("m 531.34024,302.32392 8.50453,0 -2.44361,0 2.44361,0 c 18.68783,0.0814 -1.01015,21.2132 -1.01015,21.2132 0,0 -2.56988,3.53554 -5.97915,-0.50508", transform2)).attr({
id: 'path3806-1-7-5',
parent: 'layer1',
fill: '#000000',
"fill-opacity": '1',
stroke: '#000000',
"stroke-width": '1.54',
"stroke-linecap": 'round',
"stroke-linejoin": 'round',
"stroke-miterlimit": '4',
"stroke-dasharray": 'none',
"stroke-opacity": '1'
});
if(properties.whiteNose) {
leftNose.attr("fill", "white");
rightNose.attr("fill", "white");
}
}
if(writeText) {
var textY = centerY + beaverParams.bottomPad;
var array = [];
for(var property in properties) {
array.push(property);
}
randomGenerator.shuffle(array);
for(var index = 0; index < array.length; index++) {
var name = taskStrings.names[array[index]];
if (squeezeText) {
textY += beaverParams.textYJumpSqueezed;
name = name.replace(" ", "\n");
}
paper.text(centerX, textY, name).attr(beaverParams.propertyAttr).transform(["S", beaverParams.scale, beaverParams.scale, centerX, centerY]);
textY += beaverParams.textYJump;
}
}
var overlay = paper.rect(
centerX - beaverParams.width / 2,
centerY - beaverParams.height / 2,
beaverParams.width,
beaverParams.height
);
overlay.attr({
fill: "green",
opacity: 0
});
overlay.transform(["S", beaverParams.scale, beaverParams.scale, centerX, centerY]);
return paper.setFinish();
}
function initAnswer() {
dragContainers.answerSlots = dragAndDrop2.addContainer({
ident: "answerSlots",
cx: paperParams.width / 2,
cy: answerSlotParams.centerY,
widthPlace: answerSlotParams.width,
heightPlace: answerSlotParams.height,
nbPlaces: data[level].totalCups,
dropMode: "replace",
placeBackgroundArray: [drawAnswerSlot()]
});
dragContainers.sourceBeavers = {};
for(var index = 0; index < data[level].totalCups; index++) {
var xPos = dragContainers.answerSlots.placeCenter(index)[0];
drawCup(paper2, xPos, answerSlotParams.centerY + answerSlotParams.cupYOffset, cupToLabel[index]);
dragContainers.sourceBeavers[index] = dragAndDrop2.addContainer({
ident: "sourceBeavers_" + index,
cx: xPos,
cy: answerSlotParams.centerY + answerSlotParams.bottomPad,
widthPlace: beaverParams.width * beaverParams.scale,
heightPlace: beaverParams.height * beaverParams.scale,
nbPlaces: 1,
dropMode: "replace",
placeBackgroundArray: []
});
}
fillAnswerContainers();
for(var cup = 0; cup < data[level].totalCups; cup++) {
var propertyIndex = answer.cupToIndex[cup];
if(propertyIndex === null) {
continue;
}
var property = levelProperties[propertyIndex];
var userProperties = {};
userProperties[property] = true;
dragAndDrop2.insertObjects("answerSlots", cup, [{
ident: "beaver_" + propertyIndex,
elements: [drawBeaver(paper2, 0, 0, userProperties, true, true, true)]
}]);
dragAndDrop2.removeAllObjects("sourceBeavers_" + propertyIndex);
}
}
function fillAnswerContainers() {
for(var index = 0; index < data[level].totalCups; index++) {
var properties = {};
properties[levelProperties[index]] = true;
dragAndDrop2.removeAllObjects("sourceBeavers_" + index);
dragAndDrop2.insertObjects("sourceBeavers_" + index, 0, [{
ident: "beaver_" + index,
elements: [drawBeaver(paper2, 0, 0, properties, true, true, true)]
}]);
}
}
function drawAnswerSlot() {
return paper2.rect(
- answerSlotParams.width / 2,
- answerSlotParams.height / 2,
answerSlotParams.width,
answerSlotParams.height
).attr(answerSlotParams.backgroundAttr);
}
function drawDataExperiments() {
experimentBeavers = {};
for(var iExperiment in data[level].experiments) {
experimentBeavers[iExperiment] = {
raphael: drawDataExperiment(iExperiment)
};
}
}
function drawDataExperiment(index) {
var centerX = experimentParams.firstCenterX + index * (experimentParams.beaverXPad);
var properties = {};
var experiment = data[level].experiments[index];
var leftCupX = centerX - experiment.length * cupParams.width / 2 - (experiment.length - 1) * experimentParams.cupXPad / 2 + cupParams.width / 2;
for(var iLabel in experiment) {
var cupX = leftCupX + iLabel * (cupParams.width + experimentParams.cupXPad);
var label = experiment[iLabel];
drawCup(paper1, cupX, experimentParams.beaverCenterY[level] - experimentParams.beaverTopPad, label);
properties[cupToProperty[labelToCup[label]]] = true;
}
drawBeaver(paper1, centerX, experimentParams.beaverCenterY[level], properties, data[level].showText, false);
}
function initUserExperiments() {
drawInitialExperimentBeavers();
dragContainers.beaverCups = {};
for(var index = 0; index < data[level].beavers; index++) {
var centerX = experimentParams.firstCenterX + index * (experimentParams.beaverXPad);
dragContainers.beaverCups[index] = dragAndDrop1.addContainer({
ident: "cupSlots_" + index,
cx: centerX,
cy: experimentParams.beaverCenterY[level] - experimentParams.beaverTopPad,
widthPlace: experimentParams.cupSlotWidth,
heightPlace: experimentParams.cupSlotHeight,
nbPlaces: data[level].experimentCups,
dropMode: "insertBefore",
placeBackgroundArray: [drawCupSlot()]
});
for(var iCup = 0; iCup < data[level].experimentCups; iCup++) {
var cupIndex = answer.experiments[index][iCup];
if(cupIndex === null) {
continue;
}
dragAndDrop1.insertObjects("cupSlots_" + index, iCup, [{
ident: "sourceCup_" + cupIndex,
elements: [drawCup(paper1, 0, 0, cupToLabel[cupIndex])]
}]);
}
}
var leftX = paperParams.width / 2 - data[level].totalCups * experimentParams.cupSlotWidth / 2 - (data[level].totalCups - 1) * experimentParams.cupSlotSpacing / 2 + experimentParams.cupSlotWidth / 2;
for(var cup = 0; cup < data[level].totalCups; cup++) {
var currentCenterX = leftX + cup * (experimentParams.cupSlotWidth + experimentParams.cupSlotSpacing);
dragContainers.sourceCups = dragAndDrop1.addContainer({
ident: "sourceCup_" + cup,
type: "source",
cx: currentCenterX,
cy: experimentParams.cupSourceY,
placeBackgroundArray: [],
sourceElemArray: [drawCup(paper1, 0, 0, cupToLabel[cup])]
});
}
drinkButton = new Button(
paper1,
experimentParams.drinkButton.x,
experimentParams.beaverCenterY[level] - experimentParams.beaverTopPad - experimentParams.drinkButton.height / 2,
experimentParams.drinkButton.width,
experimentParams.drinkButton.height,
taskStrings.drinkButton(data[level].beavers));
drinkButton.click(clickDrink);
reshuffleButton = new Button(
paper1,
experimentParams.reshuffleButton.x,
experimentParams.beaverCenterY[level] - experimentParams.beaverTopPad - experimentParams.reshuffleButton.height / 2,
experimentParams.reshuffleButton.width,
experimentParams.reshuffleButton.height,
taskStrings.reshuffleButton);
reshuffleButton.click(clickReshuffle);
updateButtonState();
}
function clickDrink() {
if(answer.state == "after") {
return;
}
if(!data[level].experiments) {
for(var iBeaver in answer.experiments) {
if(answer.experiments[iBeaver][0] === null) {
showFeedback(taskStrings.emptyExperiment);
return;
}
}
}
showFeedback(null);
answer.state = "after";
performUserExperiments();
updateButtonState();
}
function clickReshuffle() {
if(answer.state == "before") {
return;
}
answer.state = "before";
var experiments = answer.experiments;
answer = subTask.getDefaultAnswerObject();
answer.experiments = experiments;
randomizePotions();
drawInitialExperimentBeavers();
dragAndDrop2.removeAllObjects("answerSlots");
fillAnswerContainers();
unhighlightWrong();
updateButtonState();
}
function updateButtonState() {
if(answer.state == "before") {
reshuffleButton.hide();
drinkButton.show();
}
else {
drinkButton.hide();
reshuffleButton.show();
}
}
function drawInitialExperimentBeavers() {
if(!experimentBeavers) {
experimentBeavers = {};
}
for(var index = 0; index < data[level].beavers; index++) {
var centerX = experimentParams.firstCenterX + index * (experimentParams.beaverXPad);
if(experimentBeavers[index] && experimentBeavers[index].raphael) {
experimentBeavers[index].raphael.remove();
}
experimentBeavers[index] = {
raphael: drawBeaver(paper1, centerX, experimentParams.beaverCenterY[level], null, data[level].showText, false),
centerX: centerX
};
}
}
function performUserExperiments() {
for(var iBeaver = 0; iBeaver < data[level].beavers; iBeaver++) {
var properties = {};
var cupsIDs = dragAndDrop1.getObjects("cupSlots_" + iBeaver);
for(var iCup = 0; iCup < data[level].experimentCups; iCup++) {
var cup = dragIDToCup(cupsIDs[iCup]);
if(cup === null) {
continue;
}
var property = cupToProperty[cup];
properties[property] = true;
}
experimentBeavers[iBeaver].raphael.remove();
experimentBeavers[iBeaver].raphael = drawBeaver(paper1, experimentBeavers[iBeaver].centerX, experimentParams.beaverCenterY[level], properties, data[level].showText, false);
}
}
function drawCupSlot() {
return paper1.rect(
- experimentParams.cupSlotWidth / 2,
- experimentParams.cupSlotHeight / 2,
experimentParams.cupSlotWidth,
experimentParams.cupSlotHeight
).attr(experimentParams.cupSlotAttr);
}
function actionIfDropped1(srcCont, srcPos, dstCont, dstPos, dropType) {
showFeedback(null);
var srcSequence = dragAndDrop1.getObjects(srcCont);
// If we are dragging a cup from the cup slots outside:
if(srcCont.substring(0, 8) == "cupSlots" && dstCont == null) {
return true;
}
// If we are dragging a cup onto something, it must be the cup slots.
if(dstCont == null || dstCont.substring(0, 8) != "cupSlots") {
return false;
}
var dstSequence = dragAndDrop1.getObjects(dstCont);
// Search for rightmost index for insertion.
var newIndex = 0;
for (var i = 0; i < dstSequence.length; i++) {
if (dstSequence[i] != null) {
newIndex = i + 1;
}
}
// If this element was already here, one more slot is available.
if (srcCont == dstCont) {
newIndex--;
}
// Allow insertion in the middle of the list.
if (dstPos <= newIndex) {
return true;
}
// Only allow appending the current list, no dropping further away to the right.
if (newIndex < data[level].experimentCups) {
return DragAndDropSystem.action(dstCont, newIndex, 'insert');
}
}
function actionIfDropped2(srcCont, srcPos, dstCont, dstPos, dropType) {
showFeedback(null);
var srcSequence = dragAndDrop2.getObjects(srcCont);
// If we are dragging a beaver from the answer slots:
if(srcCont == "answerSlots") {
var cup = dragIDToCup(srcSequence[srcPos]);
if(dstCont == "answerSlots") {
return true;
}
// Dragging anywhere else means going back to the original container.
return DragAndDropSystem.action("sourceBeavers_" + cup, 0, "insert");
}
// If we are dragging a beaver from its original place:
if(srcCont.substring(0, 13) == "sourceBeavers") {
// We can only drag to the answer slots.
return dstCont == "answerSlots";
}
}
function actionIfEjected2(refElement, srcCont, srcPos) {
if(srcCont != "answerSlots") {
return null;
}
// Ejected beavers go back to their place.
var cup = dragIDToCup(refElement.ident);
return DragAndDropSystem.action("sourceBeavers_" + cup, 0, "insert");
}
function canBeTaken1(containerID, position) {
unhighlightWrong();
if(answer.state != "before") {
showFeedback(taskStrings.cupsTooLate);
return false;
}
return true;
}
function canBeTaken2(containerID, position) {
unhighlightWrong();
if(answer.state == "before") {
showFeedback(taskStrings.answerTooEarly);
return false;
}
return true;
}
function onDrop(srcContainerID, srcPos, dstContainerID, dstPos, dropType) {
var slots = dragAndDrop2.getObjects("answerSlots");
for(var cup = 0; cup < data[level].totalCups; cup++) {
answer.cupToIndex[cup] = dragIDToCup(slots[cup]);
}
if(!data[level].experiments) {
for(var iBeaver = 0; iBeaver < data[level].beavers; iBeaver++) {
slots = dragAndDrop1.getObjects("cupSlots_" + iBeaver);
for(var iCup = 0; iCup < data[level].experimentCups; iCup++) {
answer.experiments[iBeaver][iCup] = dragIDToCup(slots[iCup]);
}
}
}
}
function dragIDToCup(id) {
if(id == null) {
return null;
}
return id.split("_")[1];
}
function createFlowGraph() {
var graph = new Graph(true);
graph.addVertex("source");
graph.addVertex("sink");
var leftID, rightID;
for(var index = 0; index < data[level].totalCups; index++) {
leftID = "cup_" + index;
graph.addVertex(leftID);
graph.addEdge("source_" + leftID, "source", leftID, {capacity: 1});
rightID = "property_" + levelProperties[index];
graph.addVertex(rightID);
graph.addEdge(rightID + "_sink", rightID, "sink", {capacity: 1});
}
for(var leftIndex = 0; leftIndex < data[level].totalCups; leftIndex++) {
leftID = "cup_" + leftIndex;
for(var rightIndex = 0; rightIndex < data[level].totalCups; rightIndex++) {
rightID = "property_" + levelProperties[rightIndex];
graph.addEdge(leftID + "_" + rightID, leftID, rightID, {capacity: 1});
}
}
return graph;
}
function applyExperimentsToFlow(graph) {
var cup, property;
var leftID, rightID, edgeID;
for(var iBeaver = 0; iBeaver < data[level].beavers; iBeaver++) {
var experiment = answer.experiments[iBeaver];
var participatingCups = {};
var participatingProperties = {};
var otherCups = {};
var otherProperties = {};
// Get all cups and beaver properties that are present in the experiment.
for(var iCup = 0; iCup < data[level].experimentCups; iCup++) {
cup = experiment[iCup];
if(cup === null) {
continue;
}
participatingCups[cup] = true;
participatingProperties[cupToProperty[cup]] = true;
}
// Get all others.
for(cup = 0; cup < data[level].totalCups; cup++) {
if(!participatingCups[cup]) {
otherCups[cup] = true;
otherProperties[cupToProperty[cup]] = true;
}
}
// Remove edges from participating to non-participating.
for(cup in participatingCups) {
leftID = "cup_" + cup;
for(property in otherProperties) {
rightID = "property_" + property;
edgeID = leftID + "_" + rightID;
if(graph.isEdge(edgeID)) {
graph.removeEdge(edgeID);
}
}
}
// Remove edges from non-participating to participating.
for(cup in otherCups) {
leftID = "cup_" + cup;
for(property in participatingProperties) {
rightID = "property_" + property;
edgeID = leftID + "_" + rightID;
if(graph.isEdge(edgeID)) {
graph.removeEdge(edgeID);
}
}
}
}
}
function checkEdgeDeduction(graph, cup, property) {
var vertex1ID = "cup_" + cup;
var vertex2ID = "property_" + property;
var edgeID = vertex1ID + "_" + vertex2ID;
var edgeInfo = graph.getEdgeInfo(edgeID);
if(!graph.isEdge(edgeID)) {
return false;
}
graph.removeEdge(edgeID);
var maxFlow = graph.getMaxFlow("source", "sink").totalFlow;
graph.addEdge(edgeID, vertex1ID, vertex2ID, edgeInfo);
return maxFlow < data[level].totalCups;
}
function checkDeduction() {
var graph = createFlowGraph();
applyExperimentsToFlow(graph);
for(var cup in answer.cupToIndex) {
var index = answer.cupToIndex[cup];
if(index === null) {
continue;
}
var property = levelProperties[index];
if(!checkEdgeDeduction(graph, cup, property)) {
return {
success: false,
wrongCup: cup,
wrongProperty: property
};
}
}
return {
success: true
};
}
function getResultAndMessage() {
var numCorrect = 0;
for(var cup in answer.cupToIndex) {
var index = answer.cupToIndex[cup];
if(index !== null && cupToProperty[cup] == levelProperties[index]) {
numCorrect++;
}
}
if(!data[level].experiments) {
var deduction = checkDeduction();
if(!deduction.success) {
return {
successRate: 0,
message: taskStrings.wrongLogic,
wrongCup: deduction.wrongCup,
wrongProperty: deduction.wrongProperty
};
}
}
if(numCorrect === data[level].totalCups) {
return {
successRate: 1,
message: taskStrings.success
};
}
if (level == "hard") {
if (data[level].totalCups <= 4) {
alert("Problem with the scoring function, which assumes more than 4 potions.");
}
return {
successRate: (numCorrect - 4) / (data[level].totalCups - 4),
message: taskStrings.wrongHard(numCorrect, data[level].totalCups)
};
} else {
return {
successRate: 0,
message: taskStrings.wrong
};
}
}
function highlightWrong(cup, property) {
unhighlightWrong();
var center = dragContainers.answerSlots.placeCenter(cup);
wrongHighlighter = paper2.rect(
center[0] - answerSlotParams.width / 2,
center[1] - answerSlotParams.height / 2,
answerSlotParams.width,
answerSlotParams.height
).attr(highlightParams.attr);
}
function unhighlightWrong() {
if(wrongHighlighter) {
wrongHighlighter.remove();
}
wrongHighlighter = null;
}
function clickValidate() {
var resultAndMessage = getResultAndMessage();
if(resultAndMessage.successRate == 1) {
platform.validate("done");
return;
}
else {
if(resultAndMessage.wrongCup !== undefined) {
highlightWrong(resultAndMessage.wrongCup, resultAndMessage.wrongProperty);
}
displayHelper.validate("stay");
}
}
function showFeedback(string) {
if(string) {
$("#feedback").html(string);
$("#feedback").show();
}
else {
$("#feedback").html("");
$("#feedback").hide();
}
}
subTask.getGrade = function(callback) {
callback(getResultAndMessage());
};
}
initWrapper(initTask, ["easy", "medium", "hard"]);