. /** * The class CryptexDB loads/save the cryptex from/to database. * * @package mod_game * @copyright 2007 Vasilis Daloukas * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * The class CryptexDB loads/save the cryptex from/to database. * * @package mod_game * @copyright 2007 Vasilis Daloukas * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class CryptexDB extends CrossDB { /** * Save cryptex. * * @param stdClass $game * @param array $crossm * @param array $crossd * @param int $id * @param string $letters * * @return the saved record */ public function savecryptex( $game, &$crossm, $crossd, $id, $letters) { global $USER; CrossDB::delete_records( $id); if ((CrossDB::savecross( $game, $crossm, $crossd, $id)) == false) { return false; } $crossm->id = $id; $newrec = new stdClass(); $newrec->id = $id; $newrec->letters = $letters; if (!($cryptexid = game_insert_record( "game_cryptex", $newrec))) { print_error( 'Insert page: new page game_cryptex not inserted'); } return $newrec; } /** * Compute letters. * * @param array $crossm * @param array $crossd * * @return the letters. */ public function computeletters( $crossm, $crossd) { $letters = ''; $cols = $crossm->usedcols + 1; $letters = str_repeat('.', $crossm->usedcols).'#'; $letters = str_repeat($letters, $crossm->usedrows); $freqs1 = array(); $count1 = $count2 = 0; foreach ($crossd as $rec) { $pos = $rec->col - 1 + ($rec->row - 1) * $cols; $s = $rec->answertext; $len = game_strlen( $s); $a = array(); for ($i = 0; $i < $len; $i++) { $a[] = game_substr( $s, $i, 1); } for ($i = 0; $i < $len; $i++) { $this->setchar( $letters, $pos, $a[ $i]); $pos += ( $rec->horizontal ? 1 : $cols); $freqs1[ ++$count1] = $a[ $i]; if ($i + 1 < $len) { $freqs2[ ++$count2] = $a[ $i].$a[ $i + 1]; } } } $len = game_strlen( $letters); $spaces = 0; for ($i = 0; $i < $len; $i++) { if (game_substr( $letters, $i, 1) == '.') { $spaces++; } } $step = 1; while ($spaces) { if ($step == 1) { $step = 2; $i = array_rand( $freqs1); $this->insertchar( $letters, $crossm->usedcols, $crossm->usedrows, $freqs1[ $i], $spaces); } else { $step = 1; $i = array_rand( $freqs2); $this->insertchars( $letters, $crossm->usedcols, $crossm->usedrows, $freqs2[ $i], $spaces); } } $retletters = ""; for ($row = 0; $row < $crossm->usedrows; $row++) { $retletters .= game_substr( $letters, $cols * $row, ($cols - 1)); } return $retletters; } /** * Displays the cryptex. * * @param int $cols * @param int $rows * @param string $letters * @param string $mask * @param boolean $showsolution * @param boolean $textdir */ public function displaycryptex( $cols, $rows, $letters, $mask, $showsolution, $textdir) { echo ""; for ($row = 0; $row < $rows; $row++) { echo ""; for ($col = 0; $col < $cols; $col++) { $pos = $cols * $row + $col; $c = game_substr( $letters, $pos, 1); $m = game_substr( $mask, $pos, 1); if ($showsolution and $m > '0') { echo ""; } else if ( $m == '1') { echo ""; } else { echo ""; } } echo "\r\n"; } echo "
".$c."".$c."".$c."
"; } /** * Inserts a char. * * @param string $letters * @param int $cols * @param int $rows * @param string $char * @param int $spaces */ public function insertchar( &$letters, $cols, $rows, $char, &$spaces) { $len = game_strlen( $letters); for ($i = 0; $i < $len; $i++) { if (game_substr( $letters, $i, 1) == '.') { $this->setchar( $letters, $i, $char); $spaces--; return; } } } /** * Inserts chars. * * @param string $letters * @param int $cols * @param int $rows * @param string $char * @param int $spaces */ public function insertchars( &$letters, $cols, $rows, $char, &$spaces) { $len = game_strlen( $letters); for ($i = 0; $i < $len; $i++) { if (game_substr( $letters, $i, 1) == '.' and game_substr( $letters, $i + 1, 1) == '.' ) { $this->setchar( $letters, $i, game_substr( $char, 0, 1)); $this->setchar( $letters, $i + 1, game_substr( $char, 1, 1)); $spaces -= 2; return true; } if (game_substr( $letters, $i, 1) == '.' and game_substr( $letters, $i + $cols + 1, 1) == '.' ) { $this->setchar( $letters, $i, game_substr( $char, 0, 1)); $this->setchar( $letters, $i + $cols + 1, game_substr( $char, 1, 1)); $spaces -= 2; return true; } } return false; } /** * Gets the hash of a word. * * @param string $word * * @return the hash */ public function gethash( $word) { $x = 37; $len = count( game_strlen( $word)); for ($i = 0; $i < $len; $i++) { $x = $x xor ord( game_substr( $word, $i, 1)); } return $x; } /** * Loads the cryptex from database. * * @param array $crossm * @param string $mask * @param int $corrects * @param string $language * * @return questions */ public function loadcryptex( $crossm, &$mask, &$corrects, &$language) { global $DB; $questions = array(); $corrects = array(); $mask = str_repeat( '0', $crossm->usedcols * $crossm->usedrows); if ($recs = $DB->get_records( 'game_queries', array( 'attemptid' => $crossm->id))) { foreach ($recs as $rec) { if ($rec->questiontext == '') { $rec->questiontext = ' '; } $key = $this->gethash( $rec->questiontext).'-'.$rec->answertext.'-'.$rec->id; $questions[ $key] = $rec; $word = $rec->answertext; $pos = $crossm->usedcols * ($rec->row - 1) + ($rec->col - 1); $len = game_strlen( $word); $found = ($rec->answertext == $rec->studentanswer); for ($i = 0; $i < $len; $i++) { $c = ( $found ? '1' : '2'); if (game_substr( $mask, $pos, 1) != '1') { game_setchar( $mask, $pos, $c); } $pos += ($rec->horizontal ? 1 : $crossm->usedcols); } if ($found) { $corrects[ $rec->id] = 1; } if ($language == '') { $language = game_detectlanguage( $rec->answertext); } } ksort( $questions); } return $questions; } /** * Calls the setwords of class Cross. * * @param string $answers * @param int $maxcols * @param array $reps * * @return Cross::setwords */ public function setwords( $answers, $maxcols, $reps) { return Cross::setwords( $answers, $maxcols, $reps); } /** * Calls the computedata of class Cross. * * @param stdClass $crossm * @param stdClass $crossd * @param string $letters * @param int $minwords * @param int $maxwords * @param int $mtimelimit */ public function computedata( &$crossm, &$crossd, &$letters, $minwords, $maxwords, $mtimelimit=3) { if (!cross::computedata( $crossm, $crossd, $letters, $minwords, $maxwords, $mtimelimit)) { return false; } $letters = $this->computeletters( $crossm, $crossd); return true; } }