You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
920 lines
26 KiB
920 lines
26 KiB
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* Cross class
|
|
*
|
|
* @package mod_game
|
|
* @copyright 2007 Vasilis Daloukas
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/*
|
|
Crossing Words for
|
|
Codewalkers PHP Coding Contest of July 2002
|
|
(http://codewalkers.com/php-contest.php)
|
|
|
|
Author Àngel Fenoy from Arenys de Mar, Barcelona.
|
|
*/
|
|
|
|
/**
|
|
* The class cross computes a crossword.
|
|
*
|
|
* @package mod_game
|
|
* @copyright 2014 Vasilis Daloukas
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class Cross
|
|
{
|
|
/** @var int Contains the words and the answers. */
|
|
public $minputanswers;
|
|
/** @var The words that will be used. */
|
|
public $mwords;
|
|
|
|
/** @var Time limit for computing the cross */
|
|
public $mtimelimit = 3;
|
|
|
|
// Computed by computenextcross.
|
|
/** @var Best score post */
|
|
public $mbestcrosspos;
|
|
/** @var Best score dir */
|
|
public $mbestcrossdir;
|
|
/** @var Best score crossword */
|
|
public $mbestcrossword;
|
|
/** @var Best puzzle */
|
|
public $mbestpuzzle;
|
|
|
|
/** @var The best score as a phrase. */
|
|
public $mbests;
|
|
/** @var The best score */
|
|
public $mbestscore;
|
|
|
|
/** @var The best connectors */
|
|
public $mbestconnectors;
|
|
/** @var The best filleds */
|
|
public $mbestfilleds;
|
|
/** @var The best spaces */
|
|
public $mbestspaces;
|
|
/** @var The best n20 */
|
|
public $mbestn20;
|
|
|
|
/** @var Computed by ComputePuzzleInfo. */
|
|
public $mmincol;
|
|
/** @var Computed by ComputePuzzleInfo. */
|
|
public $mmaxcol;
|
|
/** @var Computed by ComputePuzzleInfo. */
|
|
public $mminrow;
|
|
/** @var Computed by ComputePuzzleInfo. */
|
|
public $mmaxrow;
|
|
/** @var Computed by ComputePuzzleInfo. */
|
|
public $mcletter;
|
|
/** @var Repetition of each word. */
|
|
public $mreps;
|
|
/** @var Average of repetitions. */
|
|
public $maveragereps;
|
|
|
|
/**
|
|
* Set words for computing.
|
|
*
|
|
* @param array $answers
|
|
* @param int $maxcols
|
|
* @param array $reps
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function setwords( $answers, $maxcols, $reps) {
|
|
$this->mreps = array();
|
|
foreach ($reps as $word => $r) {
|
|
$this->mreps[ game_upper( $word)] = $r;
|
|
}
|
|
$this->maveragereps = 0;
|
|
foreach ($reps as $r) {
|
|
$this->maveragereps += $r;
|
|
}
|
|
if (count( $reps)) {
|
|
$this->maveragereps /= count( $reps);
|
|
}
|
|
|
|
$this->minputanswers = array();
|
|
foreach ($answers as $word => $answer) {
|
|
$this->minputanswers[ game_upper( $word)] = $answer;
|
|
}
|
|
|
|
$this->mwords = array();
|
|
|
|
$maxlen = 0;
|
|
foreach ($this->minputanswers as $word => $answer) {
|
|
$len = game_strlen( $word);
|
|
if ($len > $maxlen) {
|
|
$maxlen = $len;
|
|
}
|
|
}
|
|
|
|
$n20 = $maxlen;
|
|
if ($n20 < 15) {
|
|
$n20 = 15;
|
|
}
|
|
|
|
$this->mn20min = round( $n20 - $n20 / 4);
|
|
$this->mn20max = round( $n20 + $n20 / 4);
|
|
if ( $this->mn20max > $maxcols and $maxcols > 0) {
|
|
$this->mn20max = $maxcols;
|
|
}
|
|
if ($this->mn20min > $this->mn20max) {
|
|
$this->mn20min = $this->mn20max;
|
|
}
|
|
|
|
$this->mwords = array();
|
|
foreach ($this->minputanswers as $word => $answer) {
|
|
$len = game_strlen( $word);
|
|
|
|
if ($len <= $this->mn20max) {
|
|
$this->mwords[] = game_upper( $word);
|
|
}
|
|
}
|
|
|
|
$this->randomize();
|
|
|
|
return count( $this->mwords);
|
|
}
|
|
|
|
/**
|
|
* Randomizes the words.
|
|
*/
|
|
public function randomize() {
|
|
$n = count( $this->mwords);
|
|
for ($j = 0; $j <= $n / 4; $j++) {
|
|
$i = array_rand( $this->mwords);
|
|
|
|
$this->swap( $this->mwords[ $i], $this->mwords[ 0]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute one crossword.
|
|
*
|
|
* @param stdClass $crossm
|
|
* @param stdClass $crossd
|
|
* @param string $letters
|
|
* @param int $minwords
|
|
* @param int $maxwords
|
|
* @param int $mtimelimit
|
|
*
|
|
* @return the crossword
|
|
*/
|
|
public function computedata( &$crossm, &$crossd, &$letters, $minwords, $maxwords, $mtimelimit=3) {
|
|
$t1 = time();
|
|
|
|
$ctries = 0;
|
|
$mbestscore = 0;
|
|
|
|
$mbestconnectors = $mbestfilleds = $mbestspaces = 0;
|
|
$mbestn20 = 0;
|
|
|
|
$nochange = 0;
|
|
$this->mtimelimit = $mtimelimit;
|
|
if ($this->mtimelimit == 30) {
|
|
$this->mtimelimit = 27;
|
|
}
|
|
for (;;) {
|
|
// Selects the size of the cross.
|
|
$n20 = mt_rand( $this->mn20min, $this->mn20max);
|
|
|
|
if (!$this->computenextcross( $n20, $ctries, $minwords, $maxwords, $nochange)) {
|
|
break;
|
|
}
|
|
|
|
$ctries++;
|
|
|
|
if (time() - $t1 > $this->mtimelimit) {
|
|
break;
|
|
}
|
|
|
|
if ($nochange > 10) {
|
|
break;
|
|
}
|
|
}
|
|
$this->computepuzzleinfo( $this->mbestn20, $this->mbestcrosspos, $this->mbestcrossdir, $this->mbestcrossword, false);
|
|
|
|
return $this->savepuzzle( $crossm, $crossd, $ctries, time() - $t1);
|
|
}
|
|
|
|
/**
|
|
* Compute the next crossword.
|
|
*
|
|
* @param int $n20
|
|
* @param int $ctries
|
|
* @param int $minwords
|
|
* @param int $maxwords
|
|
* @param int $nochange
|
|
*
|
|
* @return \moodle_url
|
|
*/
|
|
public function computenextcross( $n20, $ctries, $minwords, $maxwords, &$nochange) {
|
|
$maxw = $n20;
|
|
|
|
$n21 = $n20 + 1;
|
|
$n22 = $n20 + 2;
|
|
$n2222 = $n22 * $n22;
|
|
|
|
$basepuzzle = str_repeat('0', $n22) .
|
|
str_repeat('0' . str_repeat('.', $n20) . '0', $n20) .
|
|
str_repeat('0', $n22);
|
|
|
|
$crosspos = array();
|
|
$crossdir = array();
|
|
$crossword = array();
|
|
|
|
$magics = array();
|
|
for ($n = 2; $n < $n21; $n++) {
|
|
$a = array();
|
|
for ($r = 2; $r < ($n + 2); $r++) {
|
|
$a[] = $r;
|
|
}
|
|
|
|
uasort($a, array( $this, 'cmp_magic'));
|
|
$magics[ $n] = $a;
|
|
}
|
|
|
|
uasort($this->mwords, array( $this, 'cmp'));
|
|
|
|
$words = ';' . implode(';', $this->mwords) . ';';
|
|
|
|
$puzzle = $basepuzzle;
|
|
|
|
$row = mt_rand(3, max( 3, $n20 - 3));
|
|
$col = mt_rand(3, max( 3, $n20 - 3));
|
|
$pos = $n22 * $row + $col;
|
|
|
|
$poss = array();
|
|
$ret = $this->scan_pos($pos, 'h', true, $puzzle, $words, $magics, $poss, $crosspos, $crossdir, $crossword, $n20);
|
|
|
|
while ($s = count($poss)) {
|
|
$p = array_shift($poss);
|
|
|
|
if ($this->scan_pos($p[0], $p[1], false, $puzzle, $words, $magics, $poss, $crosspos, $crossdir, $crossword, $n20)) {
|
|
$nwords = count( $crossword);
|
|
if ($maxwords) {
|
|
if ($nwords >= $maxwords) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$nwords = count( $crossword);
|
|
if ($minwords) {
|
|
if ($nwords < $minwords) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$score = $this->computescore( $puzzle, $n20, $n22, $n2222, $nwords, $nconnectors, $nfilleds, $cspaces, $crossword);
|
|
|
|
if ($score > $this->mbestscore) {
|
|
$this->mbestcrosspos = $crosspos;
|
|
$this->mbestcrossdir = $crossdir;
|
|
$this->mbestcrossword = $crossword;
|
|
$this->mbestpuzzle = $puzzle;
|
|
|
|
$this->mbests = array('Words' => "$nwords * 5 = " . ($nwords * 5),
|
|
'Connectors' => "$nconnectors * 3 = " . ($nconnectors * 3),
|
|
'Filled in spaces' => $nfilleds,
|
|
"N20" => $n20
|
|
);
|
|
|
|
$this->mbestscore = $score;
|
|
|
|
$this->mbestconnectors = $nconnectors;
|
|
$this->mbestfilleds = $nfilleds;
|
|
$this->mbestspaces = $cspaces;
|
|
$this->mbestn20 = $n20;
|
|
$nochange = 0;
|
|
} else {
|
|
$nochange++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Compute the scrore of one crossword.
|
|
*
|
|
* @param string $puzzle
|
|
* @param int $n20
|
|
* @param int $n22
|
|
* @param int $n2222
|
|
* @param int $nwords
|
|
* @param int $nconnectors
|
|
* @param int $nfilleds
|
|
* @param int $cspaces
|
|
* @param stdClass $crossword
|
|
*
|
|
* @return \moodle_url
|
|
*/
|
|
public function computescore( $puzzle, $n20, $n22, $n2222, $nwords, &$nconnectors, &$nfilleds, &$cspaces, $crossword) {
|
|
$nconnectors = $nfilleds = 0;
|
|
$puzzle00 = str_replace('.', '0', $puzzle);
|
|
|
|
$used = 0;
|
|
for ($n = 0; $n < $n2222; $n++) {
|
|
if ($puzzle00[$n]) {
|
|
$used ++;
|
|
|
|
if (($puzzle00[$n - 1] or $puzzle00[$n + 1]) and ($puzzle00[$n - $n22] or $puzzle00[$n + $n22])) {
|
|
$nconnectors++;
|
|
} else {
|
|
$nfilleds++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$cspaces = substr_count( $puzzle, ".");
|
|
$score = ($nwords * 5) + ($nconnectors * 3) + $nfilleds;
|
|
|
|
$sumrep = 0;
|
|
foreach ($crossword as $word) {
|
|
$word = game_substr( $word, 1, -1);
|
|
|
|
if (array_key_exists( $word, $this->mreps)) {
|
|
$sumrep += $this->mreps[ $word] - $this->maveragereps;
|
|
}
|
|
}
|
|
|
|
return $score - 10 * $sumrep;
|
|
}
|
|
|
|
/**
|
|
* Compute puzzle info.
|
|
*
|
|
* @param int $n20
|
|
* @param int $crosspos
|
|
* @param int $crossdir
|
|
* @param stdClass $crossword
|
|
* @param boolean $bprint
|
|
*/
|
|
public function computepuzzleinfo( $n20, $crosspos, $crossdir, $crossword, $bprint=false) {
|
|
$bprint = false;
|
|
$n22 = $n20 + 2;
|
|
|
|
$this->mmincol = $n22;
|
|
$this->mmaxcol = 0;
|
|
$this->mminrow = $n22;
|
|
$this->mmaxrow = 0;
|
|
$this->mcletter = 0;
|
|
|
|
if (count( $crossword) == 0) {
|
|
return;
|
|
}
|
|
|
|
if ($bprint) {
|
|
echo "<br><br>PuzzleInfo n20=$n20 words=".count( $crossword)."<BR>";
|
|
}
|
|
for ($i = 0; $i < count($crosspos); $i++) {
|
|
$pos = $crosspos[ $i];
|
|
$col = $pos % $n22;
|
|
$row = floor( $pos / $n22);
|
|
$dir = $crossdir[ $i];
|
|
|
|
$len = game_strlen( $crossword[ $i]) - 3;
|
|
|
|
if ($bprint) {
|
|
echo "col=$col row=$row dir=$dir word=".$crossword[ $i]."<br>";
|
|
}
|
|
|
|
$this->mcletter += $len;
|
|
|
|
if ($col < $this->mmincol) {
|
|
$this->mmincol = $col;
|
|
}
|
|
|
|
if ($row < $this->mminrow) {
|
|
$this->mminrow = $row;
|
|
}
|
|
|
|
if ($dir == 'h') {
|
|
$col += $len;
|
|
} else {
|
|
$row += $len;
|
|
}
|
|
|
|
if ($col > $this->mmaxcol) {
|
|
$this->mmaxcol = $col;
|
|
}
|
|
if ($row > $this->mmaxrow) {
|
|
$this->mmaxrow = $row;
|
|
}
|
|
}
|
|
|
|
if ($bprint) {
|
|
echo "mincol={$this->mmincol} maxcol={$this->mmaxcol} minrow={$this->mminrow} maxrow={$this->mmaxrow}<br>";
|
|
}
|
|
|
|
if ($this->mmincol > $this->mmaxcol) {
|
|
$this->mmincol = $this->mmaxcol;
|
|
}
|
|
if ($this->mminrow > $this->mmaxrow) {
|
|
$this->mminrow = $this->mmaxrow;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save the crossword to database.
|
|
*
|
|
* @param array $crossm
|
|
* @param array $crossd
|
|
* @param int $ctries
|
|
* @param int $time
|
|
*/
|
|
public function savepuzzle( &$crossm, &$crossd, $ctries, $time) {
|
|
$n22 = $this->mbestn20 + 2;
|
|
|
|
$cols = $this->mmaxcol - $this->mmincol + 1;
|
|
$rows = $this->mmaxrow - $this->mminrow + 1;
|
|
|
|
$bswapcolrow = false;
|
|
|
|
if ($bswapcolrow) {
|
|
swap( $cols, $rows);
|
|
swap( $this->mmincol, $this->mminrow);
|
|
swap( $this->mmaxcol, $this->mmaxrow);
|
|
}
|
|
|
|
$crossm = new stdClass();
|
|
$crossm->datebegin = time();
|
|
$crossm->time = $time;
|
|
$crossm->usedcols = $cols;
|
|
$crossm->usedrows = $rows;
|
|
$crossm->words = count( $this->mbestcrosspos);
|
|
$crossm->wordsall = count( $this->minputanswers);
|
|
|
|
$crossm->createscore = $this->mbestscore;
|
|
$crossm->createtries = $ctries;
|
|
$crossm->createtimelimit = $this->mtimelimit;
|
|
$crossm->createconnectors = $this->mbestconnectors;
|
|
$crossm->createfilleds = $this->mbestfilleds;
|
|
$crossm->createspaces = $this->mbestspaces;
|
|
|
|
for ($i = 0; $i < count($this->mbestcrosspos); $i++) {
|
|
$pos = $this->mbestcrosspos[ $i];
|
|
|
|
$col = $pos % $n22;
|
|
$row = floor( ($pos - $col) / $n22);
|
|
|
|
$col += -$this->mmincol + 1;
|
|
$row += -$this->mminrow + 1;
|
|
|
|
$dir = $this->mbestcrossdir[ $i];
|
|
$word = $this->mbestcrossword[ $i];
|
|
$word = substr( $word, 1, strlen( $word) - 2);
|
|
|
|
$rec = new stdClass();
|
|
|
|
$rec->col = $col;
|
|
$rec->row = $row;
|
|
$rec->horizontal = ($dir == "h" ? 1 : 0);
|
|
|
|
$rec->answertext = $word;
|
|
$rec->questiontext = $this->minputanswers[ $word];
|
|
|
|
if ($rec->horizontal) {
|
|
$key = sprintf( 'h%10d %10d', $rec->row, $rec->col);
|
|
} else {
|
|
$key = sprintf( 'v%10d %10d', $rec->col, $rec->row);
|
|
}
|
|
|
|
$crossd[ $key] = $rec;
|
|
}
|
|
if (count( $crossd) > 1) {
|
|
ksort( $crossd);
|
|
}
|
|
|
|
return (count( $crossd) > 0);
|
|
}
|
|
|
|
/**
|
|
* Swaps two variables.
|
|
*
|
|
* @param object $a
|
|
* @param object $b
|
|
*/
|
|
public function swap( &$a, &$b) {
|
|
$temp = $a;
|
|
$a = $b;
|
|
$b = $temp;
|
|
}
|
|
|
|
/**
|
|
* Displays a crossword.
|
|
*
|
|
* @param sting $puzzle
|
|
* @param int $n20
|
|
*/
|
|
public function displaycross($puzzle, $n20) {
|
|
$n21 = $n20 + 1;
|
|
$n22 = $n20 + 2;
|
|
$n2222 = $n22 * $n22;
|
|
$n2221 = $n2222 - 1;
|
|
$n2200 = $n2222 - $n22;
|
|
|
|
$ret = "<table border=0 cellpadding=2 cellspacing=1><tr>";
|
|
for ($n = 0;; $n ++) {
|
|
$c = game_substr( $puzzle, $n, 1);
|
|
|
|
if (($m = $n % $n22) == 0 or $m == $n21 or $n < $n22 or $n > $n2200) {
|
|
$ret .= "<td class=marc> </td>";
|
|
} else if ( $c == '0') {
|
|
$ret .= "<td class=limit> </td>";
|
|
} else if ($c == '.') {
|
|
$ret .= "<td class=blanc> </td>";
|
|
} else {
|
|
if ((game_substr( $puzzle, $n - 1, 1) > '0' or
|
|
game_substr( $puzzle, $n + 1, 1) > '0') and
|
|
(game_substr( $puzzle, $n - $n22, 1) > '0'
|
|
or game_substr( $puzzle, $n + $n22, 1) > '0')) {
|
|
$ret .= "<td align=center class=connector>$c</td>";
|
|
} else {
|
|
$ret .= "<td align=center class=filled>$c</td>";
|
|
}
|
|
}
|
|
|
|
if ($n == $n2221) {
|
|
return "$ret</tr></table>";
|
|
} else if ($m == $n21) {
|
|
$ret .= "</tr><tr>";
|
|
}
|
|
}
|
|
return $ret.'</tr></table>';
|
|
}
|
|
|
|
/**
|
|
* Scans a position.
|
|
*
|
|
* @param int $pos
|
|
* @param int $dir
|
|
* @param int $valblanc
|
|
* @param string $puzzle
|
|
* @param array $words
|
|
* @param object $magics
|
|
* @param object $poss
|
|
* @param int $crosspos
|
|
* @param int $crossdir
|
|
* @param object $crossword
|
|
* @param int $n20
|
|
*
|
|
* @return true if it is ok.
|
|
*/
|
|
public function scan_pos($pos, $dir, $valblanc, &$puzzle, &$words, &$magics,
|
|
&$poss, &$crosspos, &$crossdir, &$crossword, $n20) {
|
|
|
|
$maxw = $n20;
|
|
|
|
$n22 = $n20 + 2;
|
|
$n2222 = $n22 * $n22;
|
|
|
|
if ($dir == 'h') {
|
|
$inc = 1;
|
|
if ($pos + $inc >= $n2222) {
|
|
return false;
|
|
}
|
|
$oinc = $n22;
|
|
$newdir = 'v';
|
|
} else {
|
|
$inc = $n22;
|
|
if ($pos + $inc >= $n2222) {
|
|
return false;
|
|
}
|
|
$oinc = 1;
|
|
$newdir = 'h';
|
|
}
|
|
|
|
$regex = game_substr( $puzzle, $pos, 1);
|
|
if ( ($regex == '0' or $regex == '.') and (!$valblanc)) {
|
|
return false;
|
|
}
|
|
|
|
if ((game_substr( $puzzle, $pos - $inc, 1) > '0')) {
|
|
return false;
|
|
}
|
|
|
|
if ((game_substr( $puzzle, $pos + $inc, 1) > '0')) {
|
|
return false;
|
|
}
|
|
|
|
$left = $right = 0;
|
|
for ($limita = $pos - $inc; ($w = game_substr( $puzzle, $limita, 1)) !== '0'; $limita -= $inc) {
|
|
if ($w == '.' and ((game_substr( $puzzle, $limita - $oinc, 1) > '0') or
|
|
(game_substr( $puzzle, $limita + $oinc, 1) > '0'))) {
|
|
break;
|
|
}
|
|
|
|
if (++$left == $maxw) {
|
|
$left --;
|
|
break;
|
|
}
|
|
|
|
$regex = $w . $regex;
|
|
}
|
|
|
|
for ($limitb = $pos + $inc; ($w = game_substr( $puzzle, $limitb, 1)) !== '0'; $limitb += $inc) {
|
|
if ($w == '.' and ((game_substr( $puzzle, $limitb - $oinc, 1) > '0')
|
|
or (game_substr( $puzzle, $limitb + $oinc, 1) > '0'))) {
|
|
break;
|
|
}
|
|
|
|
if (++$right == $maxw) {
|
|
$right--;
|
|
break;
|
|
}
|
|
|
|
$regex .= $w;
|
|
}
|
|
|
|
if (($lenregex = game_strlen($regex)) < 2) {
|
|
return false;
|
|
}
|
|
|
|
foreach ($magics[$lenregex] as $m => $lens) {
|
|
$ini = max(0, ($left + 1) - $lens);
|
|
$fin = $left;
|
|
|
|
$posp = max($limita + $inc, $pos - (($lens - 1 ) * $inc));
|
|
|
|
for ($posc = $ini; $posc <= $fin; $posc++, $posp += $inc) {
|
|
if (game_substr( $puzzle, $posp - $inc, 1) > '0') {
|
|
continue;
|
|
}
|
|
|
|
$w = game_substr($regex, $posc, $lens);
|
|
|
|
if (!$this->my_preg_match( $w, $words, $word)) {
|
|
continue;
|
|
}
|
|
|
|
$larr0 = $posp + ((game_strlen( $word) - 2) * $inc);
|
|
|
|
if ($larr0 >= $n2222) {
|
|
continue;
|
|
}
|
|
|
|
if (game_substr( $puzzle, $larr0, 1) > '0') {
|
|
continue;
|
|
}
|
|
|
|
$words = str_replace( $word, ';', $words);
|
|
|
|
$len = game_strlen( $word);
|
|
for ($n = 1, $pp = $posp; $n < $len - 1; $n++, $pp += $inc) {
|
|
$this->setchar( $puzzle, $pp, game_substr( $word , $n, 1));
|
|
|
|
if ($pp == $pos) {
|
|
continue;
|
|
}
|
|
|
|
$c = game_substr( $puzzle, $pp, 1);
|
|
$poss[] = array($pp, $newdir, ord( $c));
|
|
}
|
|
|
|
$crosspos[] = $posp;
|
|
$crossdir[] = ($newdir == 'h' ? 'v' : 'h');
|
|
$crossword[] = $word;
|
|
|
|
$this->setchar( $puzzle, $posp - $inc, '0');
|
|
$this->setchar( $puzzle, $pp, '0');
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* my_preg_match (backward compatibility).
|
|
*
|
|
* @param string $w
|
|
* @param string $words
|
|
* @param string $word
|
|
*
|
|
* @return true if it is ok.
|
|
*/
|
|
public function my_preg_match( $w, $words, &$word) {
|
|
$a = explode( ";", $words);
|
|
$lenw = game_strlen( $w);
|
|
foreach ($a as $test) {
|
|
if (game_strlen( $test) != $lenw) {
|
|
continue;
|
|
}
|
|
|
|
for ($i = 0; $i < $lenw; $i++) {
|
|
if (game_substr( $w, $i, 1) == '.') {
|
|
continue;
|
|
}
|
|
if (game_substr( $w, $i, 1) != game_substr( $test, $i, 1) ) {
|
|
break;
|
|
}
|
|
}
|
|
if ($i < $lenw) {
|
|
continue;
|
|
}
|
|
$word = ';'.$test.';';
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set a char to the specified position.
|
|
*
|
|
* @param string $s
|
|
* @param int $pos
|
|
* @param string $char
|
|
*/
|
|
public function setchar( &$s, $pos, $char) {
|
|
$ret = "";
|
|
|
|
if ($pos > 0) {
|
|
$ret .= game_substr( $s, 0, $pos);
|
|
}
|
|
|
|
$s = $ret . $char . game_substr( $s, $pos + 1, game_strlen( $s) - $pos - 1);
|
|
}
|
|
|
|
/**
|
|
* Show html base.
|
|
*
|
|
* @param object $crossm
|
|
* @param object $crossd
|
|
* @param boolean $showsolution
|
|
* @param boolean $showhtmlsolutions
|
|
* @param boolean $showstudentguess
|
|
* @param stdClass $context
|
|
* @param stdClass $game
|
|
*/
|
|
public function showhtml_base( $crossm, $crossd, $showsolution, $showhtmlsolutions, $showstudentguess, $context, $game) {
|
|
global $CFG, $DB;
|
|
|
|
$this->mLegendh = array();
|
|
$this->mLegendv = array();
|
|
|
|
$sret = "CrosswordWidth = {$crossm->usedcols};\n";
|
|
$sret .= "CrosswordHeight = {$crossm->usedrows};\n";
|
|
|
|
$sret .= "Words=".count( $crossd).";\n";
|
|
$swordlength = "";
|
|
$sguess = "";
|
|
$ssolutions = '';
|
|
$shtmlsolutions = '';
|
|
$swordx = "";
|
|
$swordy = "";
|
|
$sclue = "";
|
|
$lasthorizontalword = -1;
|
|
$i = -1;
|
|
$legendv = array();
|
|
$legendh = array();
|
|
|
|
if ($game->glossaryid) {
|
|
$sql = "SELECT id,course FROM {$CFG->prefix}glossary WHERE id={$game->glossaryid}";
|
|
$glossary = $DB->get_record_sql( $sql);
|
|
$cmglossary = get_coursemodule_from_instance('glossary', $game->glossaryid, $glossary->course);
|
|
$contextglossary = game_get_context_module_instance( $cmglossary->id);
|
|
}
|
|
foreach ($crossd as $rec) {
|
|
if ($rec->horizontal == false and $lasthorizontalword == -1) {
|
|
$lasthorizontalword = $i;
|
|
}
|
|
|
|
$i++;
|
|
|
|
$swordlength .= ",".game_strlen( $rec->answertext);
|
|
if ($rec->questionid != 0) {
|
|
$q = game_filterquestion(str_replace( '\"', '"', $rec->questiontext),
|
|
$rec->questionid, $context->id, $game->course);
|
|
$rec->questiontext = game_repairquestion( $q);
|
|
} else {
|
|
// Glossary.
|
|
$q = game_filterglossary(str_replace( '\"', '"', $rec->questiontext),
|
|
$rec->glossaryentryid, $contextglossary->id, $game->course);
|
|
$rec->questiontext = game_repairquestion( $q);
|
|
}
|
|
|
|
$sclue .= ',"'.game_tojavascriptstring( game_filtertext( $rec->questiontext, 0))."\"\r\n";
|
|
if ($showstudentguess) {
|
|
$sguess .= ',"'.$rec->studentanswer.'"';
|
|
} else {
|
|
$sguess .= ",''";
|
|
}
|
|
$swordx .= ",".($rec->col - 1);
|
|
$swordy .= ",".($rec->row - 1);
|
|
if ($showsolution) {
|
|
$ssolutions .= ',"'.$rec->answertext.'"';
|
|
} else {
|
|
$ssolutions .= ',""';
|
|
}
|
|
|
|
if ($showhtmlsolutions) {
|
|
$shtmlsolutions .= ',"'.base64_encode( $rec->answertext).'"';
|
|
}
|
|
|
|
$attachment = '';
|
|
|
|
$s = $rec->questiontext.$attachment;
|
|
if ($rec->horizontal) {
|
|
if (array_key_exists( $rec->row, $legendh)) {
|
|
$legendh[ $rec->row][] = $s;
|
|
} else {
|
|
$legendh[ $rec->row] = array( $s);
|
|
}
|
|
} else {
|
|
if (array_key_exists( $rec->col, $legendv)) {
|
|
$legendv[ $rec->col][] = $s;
|
|
} else {
|
|
$legendv[ $rec->col] = array( $s);
|
|
}
|
|
}
|
|
}
|
|
|
|
$letters = get_string( 'lettersall', 'game');
|
|
|
|
$this->mlegendh = array();
|
|
foreach ($legendh as $key => $value) {
|
|
if (count( $value) == 1) {
|
|
$this->mlegendh[ $key] = $value[ 0];
|
|
} else {
|
|
for ($i = 0; $i < count( $value); $i++) {
|
|
$this->mlegendh[ $key.game_substr( $letters, $i, 1)] = $value[ $i];
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->mlegendv = array();
|
|
foreach ($legendv as $key => $value) {
|
|
if (count( $value) == 1) {
|
|
$this->mlegendv[ $key] = $value[ 0];
|
|
} else {
|
|
for ($i = 0; $i < count( $value); $i++) {
|
|
$this->mlegendv[ $key.game_substr( $letters, $i, 1)] = $value[ $i];
|
|
}
|
|
}
|
|
}
|
|
|
|
ksort( $this->mlegendh);
|
|
ksort( $this->mlegendv);
|
|
|
|
$sret .= "WordLength = new Array( ".game_substr( $swordlength, 1).");\n";
|
|
$sret .= "Clue = new Array( ".game_substr( $sclue, 1).");\n";
|
|
$sguess = str_replace( ' ', '_', $sguess);
|
|
$sret .= "Guess = new Array( ".game_substr( $sguess, 1).");\n";
|
|
$sret .= "Solutions = new Array( ".game_substr( $ssolutions, 1).");\n";
|
|
if ($showhtmlsolutions) {
|
|
$sret .= "HtmlSolutions = new Array( ".game_substr( $shtmlsolutions, 1).");\n";
|
|
}
|
|
$sret .= "WordX = new Array( ".game_substr( $swordx, 1).");\n";
|
|
$sret .= "WordY = new Array( ".game_substr( $swordy, 1).");\n";
|
|
$sret .= "LastHorizontalWord = $lasthorizontalword;\n";
|
|
|
|
return $sret;
|
|
}
|
|
|
|
/**
|
|
* Compares length of two strings.
|
|
*
|
|
* @param string $a
|
|
* @param string $b
|
|
*
|
|
* @return -1 if a < b, 0 if equal
|
|
*/
|
|
public function cmp($a, $b) {
|
|
return game_strlen($b) - game_strlen($a);
|
|
}
|
|
|
|
/**
|
|
* Compares two strings with random
|
|
*
|
|
* @param string $a
|
|
* @param string $b
|
|
*
|
|
* @return -1 if a < b, 0 if equal
|
|
*/
|
|
public function cmp_magic($a, $b) {
|
|
return (game_strlen($a) + mt_rand(0, 3)) - (game_strlen($b) - mt_rand(0, 1));
|
|
}
|
|
}
|
|
|