> Main
> Blog

Automating School: Hot Potatoes Drag & Drop


HotPot's drag and drop was by far the easiest and the funniest one to automate.

Drag and drop quiz

As you can see, the developers clearly have an eye for design. For this one, I decided to check out the code first. I used inspect element on the "Tarkista" (check answers) button, which showed me that it called a CheckAnswers function. Now, I'm not exactly a webdev, so I clicked on the button to show me to the source of the event.

Which brought me to this:

Which wasn't particularly useful, but as you can see on the left, something called "attempt.php" was selected. This was literally just the webpage, but I didn't realize this at the time. It told me some error and to refresh the page when I clicked on it, so I did. It then showed to me that it was just the webpage. I then decided to search up CheckAnswers on it, and I found this disgusting pile of garbage (you don't need to read it):

function CheckAnswers(ForceQuizEvent){
	HP.onclickCheck();
	if (Locked == true){return;}
	var TotalCorrect = 0;
	Score = 0;
	var Feedback = '';
	if (AnswersTried.length > 0){AnswersTried += ' | ';}
	var i, j;
	for (i=0; i<D.length; i++){
		if (i>0){AnswersTried += ',';}
		AnswersTried += D[i][1] + '.' + D[i][2] + '';
		if ((D[i][2] == D[i][1])&&(D[i][2] > 0)){
			TotalCorrect++;
		} else {
				DC[i].SetL(DC[i].GetL() + 10);
				DC[i].Highlight();
		}
	}
	Score = Math.floor((100*(TotalCorrect-Penalties))/F.length);
	var AllDone = false;
	if (TotalCorrect == F.length) {
		AllDone = true;
	}
	if (AllDone == true){
		Feedback = YourScoreIs + ' ' + Score + '%.';
		ShowMessage(Feedback + '<br />' + CorrectResponse);
	} else {
		Feedback = YourScoreIs + ' ' + Score + '%.' + '<br />' + IncorrectResponse;
		ShowMessage(Feedback);
		Penalties++;
	}
	if (AllDone){
		var QuizEvent = HP.EVENT_COMPLETED;
	} else if (ForceQuizEvent){
		var QuizEvent = ForceQuizEvent;
	} else if (TimeOver){
		var QuizEvent = HP.EVENT_TIMEDOUT;
	} else {
		var QuizEvent = HP.EVENT_CHECK;
	}
	if (HP.end_of_quiz(QuizEvent)) {
		if (window.Interval) {
			clearInterval(window.Interval);
		}
		TimeOver = true;
		Locked = true;
		Finished = true;
	}
	if (Finished || HP.sendallclicks){
		if (QuizEvent==HP.EVENT_COMPLETED){
			setTimeout('HP_send_results('+QuizEvent+')', SubmissionTimeout);
		} else {
			HP_send_results(QuizEvent);
		}
	}
}

As you can see, it's an unreadable sack of shit. Still, from this I managed to deduce that the D array must hold something relating to the answers. After some investigation, I found out how it works.

D = [
    0: [
        0: text_of_answer,
        1: target_answer,
        2: given_answer
    ],
    1: [
        0: text_of_answer,
        1: target_answer,
        2: given_answer
    ],
    ...
]

text_of_answer is the text in the draggable box, target_answer is where you're supposed to drag it, and given_answer is where you have dragged the box. This leads to a fairly obvious solution: just set given_answer to target_answer.

for (i = 0; i < D.length; i++) {
  D[i][2] = D[i][1]
}

It worked first try.

100% points

This took me max 10 minutes, and is quite funny in its simplicity. But there's more. Look at this line:

Score = Math.floor((100*(TotalCorrect-Penalties))/F.length);

TotalCorrect is reset at the beginning of CheckAnswers, but Penalties is not. Perhaps we could set Penalties to a negative number, and that would allow us to get > 100% score.

for (i = 0; i < D.length; i++) {
  D[i][2] = D[i][1]
}
Penalties = -56789

It worked:

1135880% score

1135880% score

There were some other things I discovered too, but I've written enough.