B>C? A>C>B? B>C>A? B>A>C? C>B>A? C>A>B? e.g. size, frequency update, last edition, ... what is the color hashing used in RevertedPIM for this group, color A, B or C? might require JS keyword or URL in pages definition of word and vice versa limited to pages with definitions (e.g. some languages) list lines with "* word = definition" split word and definition pick one and display the other with alternatives extend type=pagehasexpression difficulty parameter should be linked to the counter e.g. increasing the number of possibilities (at least 1 out of 5 pages instead of at least 1 out of 3) see the hall of fame to see which exercise are the easiest do a graph traversal rather than random jumps i.e. if pick randomly from the linked pages or from the group or from the the whole wiki of the previous page consider https://research.cc.gatech.edu/inc/game-forge improve parsing discard more pages structuring e.g. PmWiki. , RecentChanges, GroupFooter, GroupHeader, Template, ... page with just one line, especially markups, typically redirections support other interfaces e.g. http://www.cs.cmu.edu/~listen/ listed in Content/Education#SeeAlso Vimperator with predictable link numbers to go faster track time require for answering each question display a clock could improve excitment, e.g. answer the maximum number of questions correctly in X minutes implemented is word X from page A, B, C or D? (type=expressioninpage) does page A contains word X, Y or Z? (type=pagehasexpression) which of those page corresponds the updates visualization V, A, B or C? (type=historyvisualization) is page A linked to page B? (type=pagelink) */ $RecipeInfo['Exercises']['Version'] = '2015-16-08'; SDV($HandleActions['Exercises'], 'Exercises'); SDV($HandleAuth['Exercises'],'read'); function Exercises($pagename, $auth){ global $ScriptUrl, $FarmD; $type = $_GET["type"]; $counter = (int) $_GET["counter"]; $difficulty = (int) $_GET["difficulty"]; list($group,$page) = explode(".",$pagename); $availableexercisetypes = array("pagelink", "expressioninpage", "pagehasexpression"); if (file_exists($FarmD."/pub/visualization/edits_per_page/" )) //the pim_functions.php generated the PJS has been loaded before $availableexercisetypes[] = "historyvisualization"; if ($type=="random" || $type=="") { $type= $availableexercisetypes[array_rand($availableexercisetypes)]; $random=true; } $result = ""; $verbatimresult = ""; $n=3; // n should be based on $difficulty, e.g. n=2+$difficulty or n=2^$difficulty // it used to determine the number of false answers // it can be overwritten per each exercise $minlinelength = 20; // this could also be consider a difficulty, the shorter the harder // it can be overwritten per each exercise // tracking the current score $counterparam = ""; if ( $counter > 0) { $counterparam = "counter=$counter&"; } if ($group == "AllPages"){ // equivalent to getting ALL pages $pages = ListPages(); } else { $pages = ListPages("/$group\./e"); } //randomly pick a page in the possible pages $sourcepage = $pages[array_rand($pages)]; unset($pages[array_search($sourcepage,$pages)]); switch ($type){ case "pagelink": $content = ReadPage($sourcepage,READPAGE_CURRENT); $links = $content["targets"]; $links_array = explode(",",$links); //note that count($links_array)<1) was not used since explode returned an array with en empty value while (($links_array[0]=="") && (count($pages)>0)) { $sourcepage = $pages[array_rand($pages)]; unset($pages[array_search($sourcepage,$pages)]); $content = ReadPage($sourcepage,READPAGE_CURRENT); $links = $content["targets"]; $links_array = explode(",",$links); } if ($links_array[0]=="") { $result .= "Unfortunately it seems no suitable page has been found for this game. "; $result .= "Try in another group or [[AllPages/AllPages?action=Exercises&type=$type&counter=$counter|in the entire wiki]]."; break; } //randomly pick a page amongst the linked pages $answers[] = $links_array[array_rand($links_array)]; unset($pages[array_search($answers[0],$links_array)]); unset($pages[array_search($answers[0],$pages)]); //randomly pick n-1 others pages which may not be amongst the list of linked page for ($i=1;$i<$n;$i++) { $answers[] = $pages[array_rand($pages)]; unset($pages[array_search($answers[$i],$pages)]); } //display $result .="Is page [[$sourcepage]] linked to "; // there should now be at least 1 needle in the haystack, the first one // note that there might be more but this is not a problem as long as the question is stated clearly. shuffle($answers); for ($i=0;$i"; $processingfile = "$ScriptUrl/pub/visualization/edits_per_page/$sourcepage.pjs"; // TODO if it does not exist, pick another page, adapt code above $answers[] = $sourcepage; //randomly pick n-1 others pages which may not be amongst the list of linked page for ($i=1;$i<$n;$i++) { $answers[] = $pages[array_rand($pages)]; unset($pages[array_search($answers[$i],$pages)]); } //display $historyvisualization = $processinglib.''; $verbatimresult = $historyvisualization; $result .="Which of those page corresponds the updates visualization:"; // note that there might be more than 1 correct answer but this is not a problem as long as the question is stated clearly. shuffle($answers); for ($i=0;$i 0 ) ) { $pickedline = $lines[array_rand($lines)]; //pregreplace("/(:.*:)/","",$pickedline); unset($lines[array_search($pickedline,$lines)]); } $actualanswer = $pickedline; $answers[] = $pickedline; for ($i=1;$i<$n;$i++) { $otherpage = $pages[array_rand($lines)]; $otherpage = $pages[array_rand($pages)]; unset($pages[array_search($otherpage,$pages)]); $content = ReadPage($otherpage,READPAGE_CURRENT); $text = $content["text"]; $lines = explode("\n",$text); $pickedline = $lines[array_rand($lines)]; unset($lines[array_search($pickedline,$lines)]); while (( strlen($pickedline) < $minlinelength ) && ( count($lines) > 0 ) ) { $pickedline = $lines[array_rand($lines)]; unset($lines[array_search($pickedline,$lines)]); } $answers[] = $pickedline; } //display $result .= "Does the page [[$sourcepage]] has expression "; shuffle($answers); for ($i=0;$i<$n;$i++) { if ($answers[$i]==$actualanswer) $hashedanswer = md5($sourcepage); else $hashedanswer = md5($answers[$i]); // hashing the answer, NOT the page, but only used to hide so should not be problematic $result .="\n* [[$pagename?action=ExercisesCheck&".$counterparam ."type=$type&source=$sourcepage&answer=$hashedanswer|$i]] [@".$answers[$i]."@] "; } $result .="\n\nClick on the answer."; break; case "expressioninpage": $content = ReadPage($sourcepage,READPAGE_CURRENT); $text = $content["text"]; $lines = explode("\n",$text); $pickedline = $lines[array_rand($lines)]; unset($lines[array_search($pickedline,$lines)]); while (( strlen($pickedline) < $minlinelength ) && ( count($lines) > 0 ) ) { $pickedline = $lines[array_rand($lines)]; unset($lines[array_search($pickedline,$lines)]); } $answers[] = $sourcepage; //randomly pick n-1 others pages which may not be amongst the list of linked page for ($i=1;$i<$n;$i++) { $otherpage = $pages[array_rand($pages)]; unset($pages[array_search($otherpage,$pages)]); $answers[] = $otherpage; } //display $result .= "Does the page expression \n* [@$pickedline@] \n\nbelongs to "; // there should now be at least 1 needle in the haystack, the first one // note that there might be more but this is not a problem as long as the question is stated clearly. shuffle($answers); for ($i=0;$i<$n;$i++) { $hashedanswer = md5($answers[$i]); if ( $i == count($answers) -1 ) $result .= " or "; $result .="[[$pagename?action=ExercisesCheck&".$counterparam ."type=$type&source=$sourcepage&answer=$hashedanswer|".$answers[$i]."]], "; } $result .="?\n\nClick on the answer."; // explode content by line "%0a" or word " " break; default: $result .= "Exercise type unknown."; } unset($availableexercisetypes[array_search($type,$availableexercisetypes)]); if (count($availableexercisetypes) > 0) { $result .= "\n\nTry other types of exercises:"; foreach ($availableexercisetypes as $e) { $result .= " [[$pagename?action=Exercises&type=$e|$e]],"; } $result .= " or a [[$pagename?action=Exercises&type=random|random]] one."; if ( $counter > 0) { $result .= " (note that it resets the counter)"; } } $result .= "\n\n%center%[-Generated by [[http://fabien.benetou.fr/MemoryRecalls/ImprovingPIM#PIMBasedExercises|PIM Based Exercises]].-]%%"; $renderedresult = MarkupToHTML($pagename, $result); print "".$verbatimresult.$renderedresult.""; //$text = RetrieveAuthSection($pagename,$auth); //$content = MarkupToHTML($pagename, $text); //print $content; } SDV($HandleActions['ExercisesCheck'], 'ExercisesCheck'); SDV($HandleAuth['ExercisesCheck'],'read'); function ExercisesCheck($pagename, $auth){ global $ScriptUrl, $FarmD, $Author; $activityfile = $FarmD."/wiki.d/.exercisesscores"; $type = $_GET["type"]; $sourcepage = $_GET["source"]; $answer = $_GET["answer"]; $counter = 0; if ( isset($_GET["counter"]) ) $counter = (int) $_GET["counter"]; $score_lower_threshold = 3; // this could also be a value relative to the top score if there is one list($group,$page) = explode(".",$pagename); if ($group == "AllPages"){ // equivalent to getting ALL pages $pages = ListPages(); } else { $pages = ListPages("/$group\./e"); } $result = ""; $verbatimresult = ""; switch ($type) { case "pagelink": $content = ReadPage($sourcepage,READPAGE_CURRENT); $links = $content["targets"]; $links_array = explode(",",$links); $formattedlinks = implode(", ",$links_array); if ( in_array($answer,$links_array) ) { $result .="Excellent! [[$sourcepage]] is indeed linked to [[$answer]]. "; $result .="Note that $formattedlinks also are. "; if ( $counter > 0) { $counter++; $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=$counter|solve yet another one]]. "; } else { $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=1|solve yet another one]]. "; } } else { $result .="No, [[$sourcepage]] is not linked to [[$answer]] "; $result .="but $formattedlinks are. "; if ($counter > 0){ $result .="\nIt means you are losing your $counter points."; // $halloffame = ExercisesResults(); // to add to $verbatimresult to motivate playing again } $result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises&type=$type|trying another time]]. "; } break; case "historyvisualization": if ( md5($sourcepage) === $answer ) { $result .="Excellent! [[$sourcepage]] indeed has that history of editions."; if ( $counter > 0) { $counter++; $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=$counter|solve yet another one]]. "; } else { $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=1|solve yet another one]]. "; } } else { $result .="No, it was the visualization of [[$sourcepage]] history of editions."; // display it again if ($counter > 0) $result .="\nIt means you are losing your $counter points."; $result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises&type=$type|trying another time]]. "; } break; case "pagehasexpression": // XXX does not work if ( md5($sourcepage) === $answer ) { $result .="Excellent! [[$sourcepage]] indeed has that expression."; if ( $counter > 0) { $counter++; $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=$counter|solve yet another one]]. "; } else { $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=1|solve yet another one]]. "; } } else { $result .="No, it was the expression of [[$sourcepage]]."; // display it again if ($counter > 0) $result .="\nIt means you are losing your $counter points."; $result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises&type=$type|trying another time]]. "; } break; case "expressioninpage": if ( md5($sourcepage) === $answer ) { $result .="Excellent! that expression did come from [[$sourcepage]]."; if ( $counter > 0) { $counter++; $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=$counter|solve yet another one]]. "; } else { $result .="\n\nSee if you can [[$pagename?action=Exercises&type=$type&counter=1|solve yet another one]]. "; } } else { $result .="No, [[$sourcepage]] was the page that expression came from."; // display it again if ($counter > 0) $result .="\nIt means you are losing your $counter points."; $result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises&type=$type|trying another time]]. "; } break; default: $result .= "Exercise type unknown, this most likely indicate that the checking for the solution has not yet been implemented, please consider notifying the author. For now try [[$pagename?action=Exercises&type=random|a random type of exercise]]."; } $result .= "\n\n%center%[-Generated by [[http://fabien.benetou.fr/MemoryRecalls/ImprovingPIM#PIMBasedExercises|PIM Based Exercises]].-]%%"; $renderedresult = MarkupToHTML($pagename, $result); print $renderedresult.$verbatimresult; if ( ( isset($Author) ) && ( $counter > $score_lower_threshold ) ) { $score = "$counter,$Author,$type,".time()."\n"; $write_result = file_put_contents($activityfile,$score, FILE_APPEND | LOCK_EX); if ($write_result < 1) print "There seems to be an error updating the score file, please check that $activityfile has write permission for httpd."; } } Markup("exercisesresults", "directives", "/\(:exercisesresults:\)/", ExercisesResults()); # markup to display results # default to all participant, not just the currently logged in user # hall of fame could be useful in collaborative learning wikis # consider first sparklines for visuals # remove failed before top score, try to group sessions and avoid results for the same function ExercisesResults(){ global $ScriptUrl, $FarmD, $Author; $availableexercisetypes = array("pagelink", "expressioninpage", "pagehasexpression", "historyvisualization"); $activityfile = $FarmD."/wiki.d/.exercisesscores"; if (file_exists($activityfile)) { $raw_scores = file_get_contents($activityfile); $allscores = explode("\n",$raw_scores); rsort($allscores,SORT_NUMERIC); //dirty, but array_filter with an lambda to test for current exercise didn't work foreach ($allscores as $score ) { list($counter,$author,$type,$time) = explode(",",$score); if ($counter.$author.$type.$time != "") $structuredanswers[] = array("counter"=>$counter,"author"=>$author,"type"=>$type,"time"=>$time); } // could instead filter by exercise and sort by highest score, listing the top3 score per per exercise $past_scores .= ""; foreach ($availableexercisetypes as $exercisetype){ $past_scores .= ""; } $past_scores .= ""; foreach ($availableexercisetypes as $exercisetype){ $past_scores .= ""; } $past_scores .= "
$exercisetype
"; $answers_added = 1; foreach ($structuredanswers as $sa){ if ($sa["type"]==$exercisetype && $answers_added<4){ $past_scores .= "".$sa["counter"]." done by ".$sa["author"]." (".date("H:m m/d/y",$sa["time"]).")
"; $answers_added++; } } $past_scores .= "
"; } else { $past_scores = "Currently no scores have been recorded. Consider starting an exercise first"; // print "Go above the minimum threshold of good answers while being logged in."; // removed for now since the threshold is at 1 } return $past_scores; } ?>