<?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/>.

/**
 * Plays the game "Sudoku".
 *
 * @package    mod_game
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

defined('MOODLE_INTERNAL') || die();

require_once(dirname(__FILE__) . '/../../../lib/questionlib.php');

/**
 * Plays the game Sudoku
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param boolean $endofgame
 * @param stdClass $context
 * @param stdClass $course
 */
function game_sudoku_continue($cm, $game, $attempt, $sudoku, $endofgame, $context, $course) {
    global $CFG, $DB, $USER;

    if ($endofgame) {
        game_updateattempts($game, $attempt, -1, true, $cm, $course);
        $endofgame = false;
    }

    if ($attempt != false && $sudoku != false) {
        return game_sudoku_play($cm, $game, $attempt, $sudoku, false, false, $context, $course);
    }

    if ($attempt == false) {
        $attempt = game_addattempt($game);
    }

    // New game.
    srand((double)microtime() * 1000000);

    $recsudoku = getrandomsudoku();
    if ($recsudoku == false) {
        throw new moodle_exception('sudoku_error', 'game', 'Empty sudoku database');
    }

    $newrec = new stdClass();
    $newrec->id = $attempt->id;
    $newrec->guess = '';
    $newrec->data = $recsudoku->data;
    $newrec->opened = $recsudoku->opened;

    $need = 81 - $recsudoku->opened;
    $closed = game_sudoku_getclosed($newrec->data);
    $n = min(count($closed), $need);
    // If the teacher set the maximum number of questions.
    if ($game->param2 > 0) {
        if ($game->param2 < $n) {
            $n = $game->param2;
        }
    }
    $recs = game_questions_selectrandom($game, CONST_GAME_TRIES_REPETITION * $n);

    if ($recs === false) {
        $sql = "DELETE FROM {game_sudoku} WHERE id={$game->id}";
        $DB->execute($sql);
        throw new moodle_exception('no_questions', 'game');
    }

    $closed = array_rand($closed, $n);

    $selectedrecs = game_select_from_repetitions($game, $recs, $n);

    if (!game_insert_record('game_sudoku', $newrec)) {
        throw new moodle_exception('error inserting in game_sudoku');
    }

    $i = 0;
    $field = ($game->sourcemodule == 'glossary' ? 'glossaryentryid' : 'questionid');
    foreach ($recs as $rec) {
        if ($game->sourcemodule == 'glossary') {
            $key = $rec->glossaryentryid;
        } else {
            $key = $rec->questionid;
        }

        if (!array_key_exists($key, $selectedrecs)) {
            continue;
        }

        $query = new stdClass();
        $query->attemptid = $newrec->id;
        $query->gamekind = $game->gamekind;
        $query->gameid = $game->id;
        $query->userid = $USER->id;
        $query->mycol = $closed[$i++];
        $query->sourcemodule = $game->sourcemodule;
        $query->questionid = $rec->questionid;
        $query->glossaryentryid = $rec->glossaryentryid;
        $query->score = 0;
        if (($query->id = $DB->insert_record('game_queries', $query)) == 0) {
            throw new moodle_exception('sudoku_error', 'game', 'error inserting in game_queries');
        }

        game_update_repetitions($game->id, $USER->id, $query->questionid, $query->glossaryentryid);
    }

    game_sudoku_play($cm, $game, $attempt, $newrec, false, false, $context, $course);
}

/**
 * Plays the game Sudoku
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param boolean $onlyshow
 * @param boolean $showsolution
 * @param stdClass $context
 * @param stdClass $course
 */
function game_sudoku_play($cm, $game, $attempt, $sudoku, $onlyshow, $showsolution, $context, $course) {
    $offsetquestions = game_sudoku_compute_offsetquestions($game->sourcemodule, $attempt, $numbers, $correctquestions);

    if ($game->toptext != '') {
        echo $game->toptext . '<br>';
    }

    game_sudoku_showsudoku(
        $sudoku->data,
        $sudoku->guess,
        true,
        $showsolution,
        $offsetquestions,
        $correctquestions,
        $cm,
        $attempt,
        $game,
        $course
    );
    switch ($game->sourcemodule) {
        case 'quiz':
        case 'question':
            game_sudoku_showquestions_quiz(
                $cm->id,
                $game,
                $attempt,
                $sudoku,
                $offsetquestions,
                $numbers,
                $correctquestions,
                $onlyshow,
                $showsolution,
                $context
            );
            break;
        case 'glossary':
            game_sudoku_showquestions_glossary(
                $cm->id,
                $game,
                $attempt,
                $sudoku,
                $offsetquestions,
                $numbers,
                $correctquestions,
                $onlyshow,
                $showsolution
            );
            break;
    }

    if ($game->bottomtext != '') {
        echo '<br>' . $game->bottomtext;
    }
}

/**
 * Returns a map with an offset and id of each question.
 *
 * @param string $sourcemodule
 * @param stdClass $attempt
 * @param int $numbers
 * @param int $correctquestions
 */
function game_sudoku_compute_offsetquestions($sourcemodule, $attempt, &$numbers, &$correctquestions) {
    global $CFG, $DB;

    $offsetquestions = [];
    if ($attempt == null) {
        return $offsetquestions;
    }
    $select = "attemptid = $attempt->id";

    $fields = 'id, mycol, score';
    switch ($sourcemodule) {
        case 'quiz':
        case 'question':
            $fields .= ',questionid as id2';
            break;
        case 'glossary':
            $fields .= ',glossaryentryid as id2';
            break;
    }
    if (($recs = $DB->get_records_select('game_queries', $select, null, '', $fields)) == false) {
        $DB->execute("DELETE FROM {$CFG->prefix}game_sudoku WHERE id={$attempt->id}");
        throw new moodle_exception('sudoku_error', 'game', 'There are no questions ' . $attempt->id);
    }

    $numbers = [];
    $correctquestions = [];
    foreach ($recs as $rec) {
        $offsetquestions[$rec->mycol] = $rec->id2;
        $numbers[$rec->id2] = $rec->mycol;
        if ($rec->score == 1) {
            $correctquestions[$rec->mycol] = 1;
        }
    }

    ksort($offsetquestions);

    return $offsetquestions;
}

/**
 * Select a sudoku randomly
 */
function getrandomsudoku() {
    global $DB;

    $count = $DB->count_records('game_sudoku_database');
    if ($count == 0) {
        require_once(dirname(__FILE__) . '/../db/importsudoku.php');

        $count = $DB->count_records('game_sudoku_database');
        if ($count == 0) {
            return false;
        }
    }

    $i = mt_rand(0, $count - 1);

    if (($recs = $DB->get_records('game_sudoku_database', null, '', '*', $i, 1)) != false) {
        foreach ($recs as $rec) {
            return $rec;
        }
    }

    return false;
}

/**
 * Get closed
 *
 * @param string $data
 */
function game_sudoku_getclosed($data) {
    $a = [];

    $n = game_strlen($data);
    for ($i = 1; $i <= $n; $i++) {
        $c = game_substr($data, $i - 1, 1);
        if ($c >= "1" && $c <= "9") {
            $a[$i] = $i;
        }
    }

    return $a;
}

/**
 * Shows the sudoku
 *
 * @param string $data
 * @param string $guess
 * @param boolean $bshowlegend
 * @param boolean $bshowsolution
 * @param int $offsetquestions
 * @param int $correctquestions
 * @param stdClass $cm
 * @param stdClass $attempt
 * @param stdClass $game
 * @param stdClass $course
 */
function game_sudoku_showsudoku(
    $data,
    $guess,
    $bshowlegend,
    $bshowsolution,
    $offsetquestions,
    $correctquestions,
    $cm,
    $attempt,
    $game,
    $course
) {
    global $CFG;

    $count = 0;

    echo "<br>\r\n";
    echo '<table border="1" style="border-collapse: separate; border-spacing: 0px;">';
    $pos = 0;
    for ($i = 0; $i <= 2; $i++) {
        echo "<tr>";
        for ($j = 0; $j <= 2; $j++) {
            echo '<td><table border="1" width="100%">';
            for ($k1 = 0; $k1 <= 2; $k1++) {
                echo "<tr>";
                for ($k2 = 0; $k2 <= 2; $k2++) {
                    $s = substr($data, $pos, 1);
                    $g = substr($guess, $pos, 1);
                    $pos++;
                    if ($g != 0) {
                        $s = $g;
                    }
                    if ($s >= "1" && $s <= "9") {
                        // Closed number.
                        if ($bshowlegend) {
                            // Show legend.
                            if ($bshowsolution == false) {
                                if (!array_key_exists($pos, $correctquestions)) {
                                    if (array_key_exists($pos, $offsetquestions)) {
                                        if ($s != $g) {
                                            $s = '<input type="submit" value="A' . $pos . '" onclick="OnCheck(' . $pos . ');" />';
                                        }
                                    } else if ($g == 0) {
                                        $s = '<input type="submit" value="" onclick="OnCheck(' . $pos . ');" />';
                                    }
                                } else {
                                    // Correct question.
                                    $count++;
                                }
                            }
                            echo '<td width=33% style="text-align: center; padding: .6em; ' .
                                ' color: red; font-weight: lighter; font-size: 1em;">' . $s . '</td>';
                        } else {
                            // Not show legend.
                            echo '<td width=33% style="text-align: center; padding: .6em;' .
                                ' color: red; font-weight: lighter; font-size: 1em;">&nbsp;</td>';
                        }
                    } else {
                        $s = strpos("-ABCDEFGHI", $s);
                        $count++;
                        echo '<td width=33% style="text-align: center; padding: .6em; ' .
                            ' color: black; font-weight: lighter; font-size: 1em;">' . $s . '</td>';
                    }
                }
                echo "</tr>";
            }
            echo "</table></td>\r\n";
        }
        echo "</tr>";
    }
    echo "</table>\r\n";
    $href = $CFG->wwwroot . '/mod/game/attempt.php?action=sudokucheckn&id=' . $cm->id;

    echo '<script type="text/javascript">
    function OnCheck(pos) {
        var s = window.prompt(' . json_encode(get_string('sudoku_guessnumber', 'game')) . ', "");
        if (s < "1") {
            return;
        }
        if (s > "9") {
            return;
        }
        window.location.href = ' . json_encode($href) . ' + "&pos=" + pos + "&num=" + s;
    }
</script>';
    // Here are the congratulations.
    if ($attempt->timefinish) {
        return $count;
    }

    if (count($offsetquestions) != count($correctquestions)) {
        return $count;
    }

    echo '<B><br>' . get_string('win', 'game') . '</B><BR>';
    echo '<br>';
    echo "<a href=\"$CFG->wwwroot/mod/game/attempt.php?id={$cm->id}&finishattempt=1\">" .
        get_string('nextgame', 'game') . '</a> &nbsp; &nbsp; &nbsp; &nbsp; ';
    echo "<a href=\"$CFG->wwwroot/course/view.php?id=$cm->course\">" . get_string('finish', 'game') . '</a> ';

    game_updateattempts($game, $attempt, 1, game_sudoku_check_found_all_numbers(), $cm, $course);

    return $count;
}

/**
 * Check that all numbers are found
 *
 * @package mod_game
 */
function game_sudoku_check_found_all_numbers() {
    return false;
}

/**
 * Get question list
 *
 * @package mod_game
 *
 * @param int $offsetquestions
 */
function game_sudoku_getquestionlist($offsetquestions) {
    $questionlist = '';
    foreach ($offsetquestions as $q) {
        if ($q != 0) {
            $questionlist .= ',' . $q;
        }
    }
    $questionlist = substr($questionlist, 1);

    if ($questionlist == '') {
        throw new moodle_exception('no_questions', 'game');
    }

    return $questionlist;
}

/**
 * Get glossary entries
 *
 * @package mod_game
 *
 * @param stdClass $game
 * @param int $offsetentries
 * @param string $entrylist
 * @param int $numbers
 */
function game_sudoku_getglossaryentries($game, $offsetentries, &$entrylist, $numbers) {
    global $DB;

    $entrylist = implode(',', $offsetentries);

    if ($entrylist == '') {
        throw new moodle_exception('sudoku_noentriesfound', 'game');
    }

    // Load the questions.
    if (!$entries = $DB->get_records_select('glossary_entries', "id IN ($entrylist)")) {
        throw new moodle_exception('sudoku_noentriesfound', 'game');
    }

    return $entries;
}

/**
 * Plays the game hangman
 *
 * @package mod_game
 *
 * @param int $id
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param int $offsetquestions
 * @param string $numbers
 * @param int $correctquestions
 * @param boolean $onlyshow
 * @param boolean $showsolution
 * @param stdClass $context
 */
function game_sudoku_showquestions_quiz(
    $id,
    $game,
    $attempt,
    $sudoku,
    $offsetquestions,
    $numbers,
    $correctquestions,
    $onlyshow,
    $showsolution,
    $context
) {
    global $CFG;

    $questionlist = game_sudoku_getquestionlist($offsetquestions);
    $questions = game_sudoku_getquestions($questionlist);

    // I will sort with the number of each question.
    $questions2 = [];
    foreach ($questions as $q) {
        $ofs = $numbers[$q->id];
        $questions2[$ofs] = $q;
    }
    ksort($questions2);

    if (count($questions2) == 0) {
        game_sudoku_showquestion_onfinish($id, $game, $attempt, $sudoku);
        return;
    }

    $found = false;
    foreach ($questions2 as $question) {
        $ofs = $numbers[$question->id];
        if (array_key_exists($ofs, $correctquestions)) {
            continue;   // I don't show the correct answers.
        }

        if ($found == false) {
            $found = true;
            // Start the form.
            echo "<form id=\"responseform\" method=\"post\" " .
                "action=\"{$CFG->wwwroot}/mod/game/attempt.php\" onclick=\"this.autocomplete='off'\">\n";
            if (($onlyshow === false) && ($showsolution === false)) {
                echo "<br><center><input type=\"submit\" name=\"submit\" value=\"" . get_string('sudoku_submit', 'game') . "\">";

                echo " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type=\"submit\" name=\"finishattempt\" value=\"" .
                get_string('sudoku_finishattemptbutton', 'game') . "\">";
            }

            // Add a hidden field with the quiz id.
            echo '<div>';
            echo '<input type="hidden" name="id" value="' . s($id) . "\" />\n";
            echo '<input type="hidden" name="action" value="sudokucheck" />';

            // Print all the questions.

            // Add a hidden field with questionids.
            echo '<input type="hidden" name="questionids" value="' . $questionlist . "\" />\n";
        }

        $number = "<a name=\"a$ofs\">A$ofs</a>";

        game_print_question($game, $question, $context);
    }

    if ($found) {
        echo "</div>";

        // Finish the form.
        echo '</div>';
        if (($onlyshow === false) && ($showsolution === false)) {
            echo "<center><input type=\"submit\" name=\"submit\" value=\"" . get_string('sudoku_submit', 'game') . "\"></center>\n";
        }

        echo "</form>\n";
    }
}

/**
 * Show the sudoku and glossaryentries.
 *
 * @package mod_game
 *
 * @param int $id
 * @param string $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param int $offsetentries
 * @param int $numbers
 * @param int $correctentries
 * @param boolean $onlyshow
 * @param boolean $showsolution
 */
function game_sudoku_showquestions_glossary(
    $id,
    $game,
    $attempt,
    $sudoku,
    $offsetentries,
    $numbers,
    $correctentries,
    $onlyshow,
    $showsolution
) {
    global $CFG;

    $entries = game_sudoku_getglossaryentries($game, $offsetentries, $questionlist, $numbers);

    // I will sort with the number of each question.
    $entries2 = [];
    foreach ($entries as $q) {
        $ofs = $numbers[$q->id];
        $entries2[$ofs] = $q;
    }
    ksort($entries2);

    if (count($entries2) == 0) {
        game_sudoku_showquestion_onfinish($id, $game, $attempt, $sudoku);
        return;
    }

    // Start the form.
    echo "<br><form id=\"responseform\" method=\"post\" " .
        "action=\"{$CFG->wwwroot}/mod/game/attempt.php\" onclick=\"this.autocomplete='off'\">\n";

    if ($onlyshow) {
        $hasquestions = false;
    } else {
        $hasquestions = (count($correctentries) < count($entries2));
    }

    if ($hasquestions) {
        echo "<center><input type=\"submit\" name=\"submit\" value=\"" . get_string('sudoku_submit', 'game') . "\"></center>\n";
    }

    // Add a hidden field with the quiz id.
    echo '<div>';
    echo '<input type="hidden" name="id" value="' . s($id) . "\" />\n";
    echo '<input type="hidden" name="action" value="sudokucheckg" />';

    // Print all the questions.

    // Add a hidden field with questionids.
    echo '<input type="hidden" name="questionids" value="' . $questionlist . "\" />\n";

    $number = 0;
    foreach ($entries2 as $entry) {
        $ofs = $numbers[$entry->id];
        if (array_key_exists($ofs, $correctentries)) {
            continue;   // I don't show the correct answers.
        }

        $query = new StdClass();
        $query->glossaryid = $game->glossaryid;
        $query->glossaryentryid = $entry->id;
        $s = '<b>A' . $ofs . '.</b> ' . game_show_query($game, $query, $entry->definition, 0) . '<br>';
        if ($showsolution) {
            $s .= get_string('answer') . ': ';
            $s .= "<input type=\"text\" name=\"resp{$entry->id}\" value=\"$entry->concept\"size=30 /><br>";
        } else if ($onlyshow === false) {
            $s .= get_string('answer') . ': ';
            $s .= "<input type=\"text\" name=\"resp{$entry->id}\" size=30 /><br>";
        }
        echo $s . "<hr>\r\n";
    }

    echo "</div>";

    // Finish the form.
    if ($hasquestions) {
        echo "<center><input type=\"submit\" name=\"submit\" value=\"" . get_string('sudoku_submit', 'game') . "\"></center>\n";
    }

    echo "</form>\n";
}

/**
 * Show question onfinish
 *
 * @package mod_game
 *
 * @param int $id
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 */
function game_sudoku_showquestion_onfinish($id, $game, $attempt, $sudoku) {
    global $CFG;

    echo '<B>' . get_string('win', 'game') . '</B><BR>';
    echo '<br>';
    echo "<a href=\"{$CFG->wwwroot}/mod/game/attempt.php?id=$id\">" .
        get_string('nextgame', 'game') . '</a> &nbsp; &nbsp; &nbsp; &nbsp; ';
    echo "<a href=\"{$CFG->wwwroot}?id=$id\">" . get_string('finish', 'game') . '</a> ';
}

/**
 * Check answers
 *
 * @package mod_game
 */
function game_sudoku_checkanswers() {
    $responses = data_submitted();

    $actions = question_extract_responses($questions, $responses, $event);
}

/**
 * Checks questions
 *
 * @package mod_game
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param boolean $finishattempt
 * @param stdClass $course
 * @param stdClass $context
 */
function game_sudoku_check_questions($cm, $game, $attempt, $sudoku, $finishattempt, $course, $context) {
    global $DB;

    $responses = data_submitted();

    $offsetquestions = game_sudoku_compute_offsetquestions($game->sourcemodule, $attempt, $numbers, $correctquestions);

    $questionlist = game_sudoku_getquestionlist($offsetquestions);

    $questions = game_sudoku_getquestions($questionlist);

    foreach ($questions as $question) {
        $query = new stdClass();

        $select = "attemptid=$attempt->id";
        $select .= " AND questionid=$question->id";

        if (($query->id = $DB->get_field_select('game_queries', 'id', $select)) == 0) {
            die("problem game_sudoku_check_questions (select=$select)");
            continue;
        }

        $grade = game_grade_responses($question, $responses, 100, $answertext, $answered);
        if ($answered == false) {
            continue;
        }
        if ($grade < 99) {
            // Wrong answer.
            game_update_queries($game, $attempt, $query, $grade / 100, $answertext);
            continue;
        }

        // Correct answer.
        game_update_queries($game, $attempt, $query, 1, $answertext);
    }

    game_sudoku_check_last($cm, $game, $attempt, $sudoku, $finishattempt, $course);
}

/**
 * Check glossary entries
 *
 * @package mod_game
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param boolean $finishattempt
 * @param stdClass $course
 */
function game_sudoku_check_glossaryentries($cm, $game, $attempt, $sudoku, $finishattempt, $course) {
    global $DB;

    $responses = data_submitted();

    // This function returns offsetentries, numbers, correctquestions.
    $offsetentries = game_sudoku_compute_offsetquestions($game->sourcemodule, $attempt, $numbers, $correctquestions);

    $entrieslist = game_sudoku_getquestionlist($offsetentries);

    // Load the glossary entries.
    if (!($entries = $DB->get_records_select('glossary_entries', "id IN ($entrieslist)"))) {
        throw new moodle_exception('noglossaryentriesfound', 'game');
    }
    foreach ($entries as $entry) {
        $answerundefined = optional_param('resp' . $entry->id, 'undefined', PARAM_TEXT);
        if ($answerundefined == 'undefined') {
            continue;
        }
        $answer = optional_param('resp' . $entry->id, '', PARAM_TEXT);
        if ($answer == '') {
            continue;
        }
        if (game_upper($entry->concept) != game_upper($answer)) {
            continue;
        }
        // Correct answer.
        $select = "attemptid=$attempt->id";
        $select .= " AND glossaryentryid=$entry->id AND mycol>0";
        // Check the student guesses not source glossary entry.
        $select .= " AND questiontext is null";

        $query = new stdClass();
        if (($query->id = $DB->get_field_select('game_queries', 'id', $select)) == 0) {
            echo "not found $select<br>";
            continue;
        }

        game_update_queries($game, $attempt, $query, 1, $answer);
    }

    game_sudoku_check_last($cm, $game, $attempt, $sudoku, $finishattempt, $course);

    return true;
}

/**
 * This is the last function after submiting the answers.
 *
 * @package mod_game
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param boolean $finishattempt
 * @param stdClass $course
 */
function game_sudoku_check_last($cm, $game, $attempt, $sudoku, $finishattempt, $course) {
    global $CFG, $DB;

    $correct = $DB->get_field_select('game_queries', 'COUNT(*) AS c', "attemptid=$attempt->id AND score > 0.9");
    $all = $DB->get_field_select('game_queries', 'COUNT(*) AS c', "attemptid=$attempt->id");

    if ($all) {
        $score = $correct / $all;
    } else {
        $score = 0;
    }
    game_updateattempts($game, $attempt, $score, $finishattempt, $cm, $course);
}

/**
 * Check number
 *
 * @package mod_game
 *
 * @param int $id
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $sudoku
 * @param int $pos
 * @param int $num
 * @param stdClass $context
 * @param stdClass $course
 */
function game_sudoku_check_number($id, $game, $attempt, $sudoku, $pos, $num, $context, $course) {
    global $DB;

    $correct = game_substr($sudoku->data, $pos - 1, 1);
    if ($correct != $num) {
        game_sudoku_play($id, $game, $attempt, $sudoku, false, false, $context, $course);
        return;
    }

    $leng = game_strlen($sudoku->guess);
    $lend = game_strlen($sudoku->data);
    if ($leng < $lend) {
        $sudoku->guess .= str_repeat(' ', $lend - $leng);
    }
    game_setchar($sudoku->guess, $pos - 1, $correct);

    if (!$DB->set_field_select('game_sudoku', 'guess', $sudoku->guess, "id=$sudoku->id")) {
        throw new moodle_exception('sudoku_error', 'game', 'game_sudoku_check_number: Cannot update table game_sudoku');
    }

    game_sudoku_play($id, $game, $attempt, $sudoku, false, false, $context, $course);
}
