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

/*
 * Edit History:
 *
 *  Dick Munroe (munroe@csworks.com) 02-Nov-2005
 *      Initial version created.
 *
 *  Dick Munroe (munroe@csworks.com) 12-Nov-2005
 *      Allow initialzePuzzle to accept a file name in addition
 *      to a resource.  Windows doesn't do file redirection properly
 *      so the examples have to be able to handle a file NAME as
 *      input as well as a redirected file.
 *      Allow initializePuzzle to accept a string of 81 characters.
 *
 *  Dick Munroe (munroe@csworks.com) 13-Nov-2005
 *	It appears that getBoardAsString screws up somehow.  Rewrite it.
 *
 *  Dick Munroe (munroe@csworks.com) 16-Nov-2005
 *     Add a "pair" inference.
 *	Dick Munroe (munroe@csworks.com) 17-Nov-2005
 *		Add comments to input files.
 *		There was a bug in applyTuple that caused premature exiting of the inference
 *		engine.
 *		If SDD isn't present, don't display error.
 *
 *	Dick Munroe (munroe@csworks.com) 19-Nov-2005
 *		Add a new tuple inference.
 *		Do a ground up ObjectS oriented redesign to make the addition of arbitrary
 *		inferences MUCH easier.
 *		Get the printing during solving right.
 *		Somehow array_equal developed a "bug".
 *
 *	Dick Munroe (munroe@csworks.com) 22-Nov-2005
 *		Add n,n+1 tuple recognition for n=2.
 *		Restructure inference engine to get maximum benefit out of each pass.
 *
 *	Dick Munroe (munroe@csworks.com) 28-Nov-2005
 *		Attempt to build harder Sudoku by implementing a coupling coefficient
 *		attempting to distribute clues more optimally.
 */

/**
 * This class is used by Sudoku game.
 *
 * @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("SDD/class.SDD.php");

/*
 * @author Dick Munroe <munroe@csworks.com>
 * @copyright copyright @ 2005 by Dick Munroe, Cottage Software Works, Inc.
 * @license http://www.csworks.com/publications/ModifiedNetBSD.html
 */

/**
 * Basic functionality needed for ObjectSs in the Sudoku solver.
 *
 * @package    mod_game
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class objects {
    /**
     * Are two array's equal (have the same contents).
     *
     * @param array $thearray1
     * @param array $thearray2
     * @return boolean
     */
    public function array_equal($thearray1, $thearray2) {
        if (!(is_array($thearray1) && is_array($thearray2))) {
            return false;
        }

        if (count($thearray1) != count($thearray2)) {
            return false;
        }

        $xxx = array_diff($thearray1, $thearray2);

        return (count($xxx) == 0);
    }

    /**
     * Deep copy anything.
     *
     * @param array $thearray [optional] Something to be deep copied.  Default is the current
     *                        ObjectS.
     * @return mixed The deep copy of the input.  All references embedded within
     *               the array have been resolved into copies allowing things like the
     *               board array to be copied.
     */
    public function deepcopy($thearray = null) {
        if ($thearray === null) {
            return unserialize(serialize($this));
        } else {
            return unserialize(serialize($thearray));
        }
    }
}

/**
 * The individual cell on the Sudoku board.
 *
 * These cells aren't restricted to 9x9 Sudoku (although pretty much everything else
 * at the moment).  This class provides the state manipulation and searching capabilities
 * needed by the inference engine (class RCS).
 *
 * @package    mod_game
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class cell extends objects {
    /** @var $r */
    protected $r;
    /** @var $c */
    protected $c;

    /** @var $state */
    protected $state = [];
    /** @var $applied */
    protected $applied = false;

    /**
     * Constructor
     *
     * @param integer $inpr row address of this cell (not used, primarily for debugging purposes).
     * @param integer $inpc column address of this cell (ditto).
     * @param integer $nstates The number of states each cell can have.  Looking forward to
     *                         implementing Super-doku.
     */
    public function init($inpr, $inpc, $nstates = 9) {
        $this->r = $inpr;
        $this->c = $inpc;

        for ($i = 1; $i <= $nstates; $i++) {
            $this->state[$i] = $i;
        }
    }

    /**
     * This cell has been "applied", i.e., solved, to the board.
     */
    public function applied() {
        $this->applied = true;
    }

    /**
     * Only those cells which are not subsets of the tuple have the
     * contents of the tuple removed.
     *
     * apply a 23Tuple to a cell.
     * @param array $atuple the tuple to be eliminated.
     */
    public function apply23tuple($atuple) {
        if (is_array($this->state)) {
            $xxx = array_intersect($this->state, $atuple);
            if ((count($xxx) > 0) && (count($xxx) != count($this->state))) {
                return $this->un_set($atuple);
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * For more details on the pair tuple algorithm, see RCS::pairSolution.
     *
     * Remove all values in the tuple, but only if the cell is a superset.
     * @param array $atuple to be eliminated from the cell's state.
     */
    public function applytuple($atuple) {
        if (is_array($this->state)) {
            if (!$this->array_equal($atuple, $this->state)) {
                return $this->un_set($atuple);
            }
        }

        return false;
    }

    /**
     * Return the string representation of the cell.
     *
     * @param boolean $theflag true if the intermediate states of the cell are to be visible.
     *
     * @return string
     */
    public function asstring($theflag = false) {
        if (is_array($this->state)) {
            if (($theflag) || (count($this->state) == 1)) {
                return implode(", ", $this->state);
            } else {
                return " ";
            }
        } else {
            return $this->state;
        }
    }

    /**
     * Assert pending solution.
     * Used to make sure that solved positions show up at print time.
     * The value is used as a candidate for "slicing and dicing" by elimination in
     * Sudoku::newSolvedPosition.
     *
     * @param integer $value The value for the solved position.
     */
    public function flagsolvedposition($value) {
        $this->state = [$value => $value];
    }

    /**
     * return the state of a cell.
     *
     * @return mixed Either solved state or array of state pending solution.
     */
    public function &getstate() {
        return $this->state;
    }

    /**
     * Has the state of this cell been applied to the board.
     *
     * @return boolean True if it has, false otherwise.  Implies that IsSolved is true as well.
     */
    public function isapplied() {
        return $this->applied;
    }

     /**
      * Has this cell been solved?
      *
      * @return boolean True if this cell has hit a single state.
      */
    public function issolved() {
        return !is_array($this->state);
    }

    /**
     * This is used primarily by the pretty printer, but has other applications in the code.
     *
     * Return information about the state of a cell.
     *
     * @return integer 0 => the cell has been solved.
     *                 1 => the cell has been solved but not seen a solved.
     *                 2 => the cell has not been solved.
     */
    public function solvedstate() {
        if (is_array($this->state)) {
            if (count($this->state) == 1) {
                return 1;
            } else {
                return 2;
            }
        } else {
            return 0;
        }
    }

    /**
     * Eliminate one or more values from the state information of the cell.
     *
     * This is the negative inference of Sudoku.  By eliminating values the
     * cells approach solutions.  Once a cell has been completely eliminated,
     * the value causing the complete elimination must be the solution and the
     * cell is promoted into the solved state.
     *
     * @param mixed $thevalues or values to be removed from the cell state.
     *
     * @return boolean True if the cell state was modified, false otherwise.
     */
    public function un_set($thevalues) {
        if (is_array($thevalues)) {
            $thereturn = false;

            foreach ($thevalues as $thevalue) {
                $thereturn |= $this->un_set($thevalue);
            }

            return $thereturn;
        }

        if (is_array($this->state)) {
            $thereturn = isset($this->state[$thevalues]);
            unset($this->state[$thevalues]);
            if (count($this->state) == 0) {
                $this->state = $thevalues;
            }
            return $thereturn;
        } else {
            return false;
        }
    }
}

/**
 * The individual row column or square on the Sudoku board.
 *
 * package Sudoku
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class rcs extends ObjectS {
    /** @var theindex */
    protected $theindex;

    /** @var therow */
    protected $therow = [];

    /** @var theheader */
    protected $theheader = "";

    /** @var thetag */
    protected $thetag = "";

    /**
     * Constructor
     *
     *  This interface is what limts things to 9x9 Sudoku currently
     * @param string $thetag "Row", "Column", "Square", used primarily in debugging.
     * @param integer $theindex 1..9, where is this on the board.  Square are numbered top left, ending bottom right
     * @param ObjectS $a1 of class Cell.
     * @param ObjectS $a2 of class Cell.
     * @param ObjectS $a3 of class Cell.
     * @param ObjectS $a4 of class Cell.
     * @param ObjectS $a5 of class Cell.
     * @param ObjectS $a6 of class Cell.
     * @param ObjectS $a7 of class Cell.
     * @param ObjectS $a8 of class Cell.
     * @param ObjectS $a9 of class Cell.
     */
    public function init($thetag, $theindex, &$a1, &$a2, &$a3, &$a4, &$a5, &$a6, &$a7, &$a8, &$a9) {
        $this->thetag = $thetag;
        $this->theindex = $theindex;
        $this->therow[1] = &$a1;
        $this->therow[2] = &$a2;
        $this->therow[3] = &$a3;
        $this->therow[4] = &$a4;
        $this->therow[5] = &$a5;
        $this->therow[6] = &$a6;
        $this->therow[7] = &$a7;
        $this->therow[8] = &$a8;
        $this->therow[9] = &$a9;
    }

    /**
     * Calculate the coupling for a cell within the row/column/square.
     *
     * This is a placeholder to be overridden to calculate the "coupling" for
     * a cell.  Coupling is defined to be the sum of the sizes of the intersection
     * between this cell and all others in the row/column/square.  This provides
     * a metric for deciding placement of clues within puzzles.  In effect, this
     * forces the puzzle generator to select places for new clues depending upon
     * how little information is changed by altering the state of a cell.  The larger
     * the number returned by the coupling, function, the less information is currently
     * available for the state of the cell.  By selecting areas with the least information
     * the clue sets are substantially smaller than simple random placement.
     *
     * @param integer $therow the row coordinate on the board of the cell.
     * @param integer $thecolumn the column coordinate on the board of the cell.
     * @return integer the degree of coupling between the cell and the rest of the cells
     *      within the row/column/square.
     */
    public function coupling($therow, $thecolumn) {
        return 0;
    }

    /**
     * Get a reference to the specified cell.
     *
     * @param int $i
     *
     * @return reference to ObjectS of class Cell.
     */
    public function &getcell($i) {
        return $this->therow[$i];
    }

    /**
     * Get the header set by the last call to doAnInference.
     */
    public function getheader() {
        return $this->theheader;
    }

    /**
     * Check to see if the RCS contains a valid state.
     *
     * @return boolean True if the state of the RCS could be part of a valid
     *          solution, false otherwise.
     */
    public function validatesolution() {
        $thenewset = [];

        foreach ($this->therow as $thecell) {
            if ($thecell->solvedstate() == 0) {
                $thenewset[] = $thecell->getstate();
            }
        }

        $xxx = array_unique($thenewset);

        return (count($xxx) == count($this->therow));
    }
}

/**
 * Row ObjectS.
 *
 * package Sudoku
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class r extends rcs {
    /**
     * Constructor
     *
     * @param string $theTag "Row", "Column", "Square", used primarily in debugging.
     * @param integer $theIndex 1..9, where is this on the board.  Square are numbered top
     *                          left, ending bottom right
     * @param ObjectS $a1..9 of class Cell.  The cells comprising this entity.  This interface is what
     *                                      limts things to 9x9 Sudoku currently.
     */

    /**
     * see RCS::coupling
     *
     * @param int $therow
     * @param int $thecolumn
     */
    public function coupling($therow, $thecolumn) {
        return $thestate = $this->coupling1($thecolumn);
    }

    /**
     * Heavy lifting for row/column coupling calculations.
     *
     * RCS::coupling1
     * @param integer $theindex the index of the cell within the row or column.
     *
     * @return integer the "coupling coefficient" for the cell.  The sum of the
     *          sizes of the intersection between this and all other
     *          cells in the row or column.
     */
    protected function coupling1($theindex) {
        $thecommonstate =& $this->getCell($theindex);
        $thecommonstate =& $thecommonstate->getstate();

        $thecoupling = 0;

        for ($i = 1; $i <= count($this->therow); $i++) {
            if ($i != $theindex) {
                $thecell =& $this->getcell($i);
                if ($thecell->solvedstate() != 0) {
                    $thecoupling += count(array_intersect($thecommonstate, $thecell->getstate()));
                }
            }
        }

        return $thecoupling;
    }
}

/**
 * The column ObjectS.
 *
 * package Sudoku
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class c extends r {
    /**
     * Constructor
     *
     * @param string $theTag "Row", "Column", "Square", used primarily in debugging.
     * @param integer $theIndex 1..9, where is this on the board.  Square are numbered top
     *                          left, ending bottom right
     * @param ObjectS $a1..9 of class Cell.  The cells comprising this entity.  This interface is what
     *          limts things to 9x9 Sudoku currently.
     */

    /**
     * see R::coupling
     *
     * @param int $therow
     * @param int $thecolumn
     */
    public function coupling($therow, $thecolumn) {
        return $thestate = $this->coupling1($therow);
    }
}

/**
 * The Square ObjectS.
 *
 * package Sudoku
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class s extends rcs {
    /**
     * The cells within the 3x3 sudoku which participate in the coupling calculation for a square.
     * Remember that the missing cells have already participated in the row or column coupling
     * calculation.
     *
     * @var array
     */
    protected $thecouplingorder = [ 1 => [5, 6, 8, 9],
        2 => [4, 6, 7, 9],
        3 => [4, 5, 7, 8],
        4 => [2, 3, 8, 9],
        5 => [1, 3, 7, 9],
        6 => [1, 2, 7, 8],
        7 => [2, 3, 5, 6],
        8 => [1, 3, 4, 6],
        9 => [1, 2, 4, 5]];

    /**
     * Constructor
     *
     * @param string $theTag "Row", "Column", "Square", used primarily in debugging.
     * @param integer $theIndex 1..9, where is this on the board.  Square are numbered top
     *                          left, ending bottom right
     * @param ObjectS $a1..9 of class Cell.  The cells comprising this entity.  This interface is what
     *                                      limits things to 9x9 Sudoku currently.
     */

    /**
     * see RCS::coupling
     *
     * @param int $therow
     * @param int $thecolumn
     */
    public function coupling($therow, $thecolumn) {
        $theindex = ((($therow - 1) % 3) * 3) + (($thecolumn - 1) % 3) + 1;
        $thecommonstate =& $this->getcell($theindex);
        $thecommonstate =& $thecommonstate->getstate();

        $thecoupling = 0;

        foreach ($this->thecouplingorder[$theindex] as $i) {
            $thecell =& $this->getcell($i);
            if ($thecell->solvedstate() != 0) {
                $thecoupling += count(array_intersect($thecommonstate, $thecell->getstate()));
            }
        }

        return $thecoupling;
    }
}

/**
 * Solve and generate Sudoku puzzles.
 *
 * Solve and generate Sudoku.  A simple output interface is provided for
 * web pages.  The primary use of this class is as infra-structure for
 * Sudoku game sites.
 *
 * The solver side of this class (solve) relies on the usual characteristic
 * of logic puzzles, i.e., at any point in time there is one (or more)
 * UNIQUE solution to some part of the puzzle.  This solution can be
 * applied, then iterated upon to find the next part of the puzzle.  A
 * properly constructed Sudoku can have only one solution which guarangees
 * that this is the case. (Sudoku with multiple solutions will always
 * require guessing at some point which is specifically disallowed by
 * the rules of Sudoku).
 *
 * While the solver side is algorithmic, the generator side is much more
 * difficult and, in fact, the generation of Sudoku appears to be NP
 * complete.  That being the case, I observed that most successful
 * generated initial conditions happened quickly, typically with < 40
 * iterations.  So the puzzle generator runs "for a while" until it
 * either succeeds or doesn't generated a solveable puzzle.  If we get
 * to that position, I just retry and so far I've always succeeded in
 * generating an initial state.  Not guarateed, but in engineering terms
 * "close enough".
 *
 * package Sudoku
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class sudoku extends ObjectS {
    /** @var array of ObjectSs of type Cell. */
    protected $theboard = [];

    /** @var bool True if debugging output is to be provided during a run. */
    protected $thedebug = false;

    /** @var ObjectS of type R An array of RCS ObjectSs, one ObjectS for each row. */
    protected $therows = [];

    /** @var ObjectS of type C An array of RCS ObjectSs, one ObjectS for each Column. */
    private $thecolumns = [];

    /** @var ObjectS of type S An array of RCS ObjectSs, one ObjectS for each square. */
    protected $thesquares = [];

    /** @var int. Used during puzzle generation for debugging output.  There may
     * eventually be some use of theLevel to figure out where to stop
     * the backtrace when puzzle generation fails.
     */
    protected $thelevel = 0;

    /** @var int. Used during puzzle generation to determine when the generation
     * will fail.  Failure, in this case, means to take a LONG time.  The
     * backtracing algorithm used in the puzzle generator will always find
     * a solution, it just might take a very long time.  This is a way to
     * limit the damage before taking another guess.
     */
    protected $themaxiterations = 50;

    /** @var int. Used during puzzle generation to limit the number of trys at
     * generation a puzzle in the event puzzle generation fails
     */
    protected $thetrys = 10;

    /** @var int. Used during puzzle generation to count the number of iterations
     * during puzzle generation.  It the number gets above $theMaxIterations,
     * puzzle generation has failed and another try is made.
     */
    protected $thegenerationiterations = 0;

    /**
     * Constructor
     *
     * @param boolean $thedebug
     */
    public function init($thedebug = false) {
        $this->thedebug = $thedebug;

        for ($i = 1; $i <= 9; $i++) {
            for ($j = 1; $j <= 9; $j++) {
                $this->theboard[$i][$j] = new cell();
                $this->theboard[$i][$j].init($i, $j);
            }
        }

        $this->buildrcs();
    }

    /**
     * Apply a pending solved position to the row/square/column.
     *
     * At this point, the board has been populated with any pending solutions.
     * This applies the "negative" inference that no row, column, or square
     * containing the value within the cell.
     *
     * @param integer $row The row of the board's element whose value is now fixed.
     * @param integer $col The column of the board's element whose value is now fixed.
     */
    protected function applysolvedposition($row, $col) {
        $thevalue = $this->theboard[$row][$col]->getstate();

        /*
         * No other cell in the row, column, or square can take on the value "value" any longer.
         */

        $i = (((int)(($row - 1) / 3)) * 3);
        $i = $i + ((int)(($col - 1) / 3)) + 1;

        $this->therows[$row]->un_set($thevalue);

        $this->thecolumns[$col]->un_set($thevalue);

        $this->thesquares[$i]->un_set($thevalue);
    }

    /**
     * Apply all pending solved positions to the board.
     *
     * @return boolean True if at least one solved position was applied, false
     *                 otherwise.
     */
    protected function applysolvedpositions() {
        $thereturn = false;

        for ($i = 1; $i <= 9; $i++) {
            for ($j = 1; $j <= 9; $j++) {
                if (!$this->theboard[$i][$j]->isapplied()) {
                    if ($this->theboard[$i][$j]->solvedstate() == 0) {
                        $this->applysolvedposition($i, $j);

                        /*
                         * Update the solved position matrix and make sure that the board actually
                         * has a value in place.
                         */

                        $this->theboard[$i][$j]->applied();
                        $thereturn = true;
                    }
                }
            }
        }

        return $thereturn;
    }

    /**
     * build the row/column/square structures for the board.
     */
    protected function buildrcs() {
        for ($i = 1; $i <= 9; $i++) {
            $this->therows[$i] = new r(
                "Row",
                $i,
                $this->theboard[$i][1],
                $this->theboard[$i][2],
                $this->theboard[$i][3],
                $this->theboard[$i][4],
                $this->theboard[$i][5],
                $this->theboard[$i][6],
                $this->theboard[$i][7],
                $this->theboard[$i][8],
                $this->theboard[$i][9]
            );
            $this->thecolumns[$i] = new C(
                "Column",
                $i,
                $this->theboard[1][$i],
                $this->theboard[2][$i],
                $this->theboard[3][$i],
                $this->theboard[4][$i],
                $this->theboard[5][$i],
                $this->theboard[6][$i],
                $this->theboard[7][$i],
                $this->theboard[8][$i],
                $this->theboard[9][$i]
            );
            $r = ((int)(($i - 1) / 3)) * 3;
            $c = (($i - 1) % 3) * 3;

            $this->thesquares[$i] = new S(
                "Square",
                $i,
                $this->theboard[$r + 1][$c + 1],
                $this->theboard[$r + 1][$c + 2],
                $this->theboard[$r + 1][$c + 3],
                $this->theboard[$r + 2][$c + 1],
                $this->theboard[$r + 2][$c + 2],
                $this->theboard[$r + 2][$c + 3],
                $this->theboard[$r + 3][$c + 1],
                $this->theboard[$r + 3][$c + 2],
                $this->theboard[$r + 3][$c + 3]
            );
        }
    }

    /**
     * Initialize Sudoku puzzle generation and generate a puzzle.
     *
     * Turns out that while the solution of Sudoku is mechanical, the creation of
     * Sudoku is an NP-Complete problem.  Which means that I can use the inference
     * engine to help generate puzzles, but I need to test the solution to see if
     * I've gone wrong and back up and change my strategy.  So something in the
     * recursive descent arena will be necessary.  Since the generation can take
     * a long time to force a solution, it's easier to probe for a solution
     * if you go "too long".
     *
     * @param integer $thedifficultylevel [optional] Since virtually everybody who
     *                plays sudoku wants a variety of difficulties this controls that.
     *                1 is the easiest, 10 the most difficult.  The easier Sudoku have
     *                extra information.
     * @param integer $themaxiterations [optional] Controls the number of iterations
     *                before the puzzle generator gives up and trys a different set
     *                of initial parameters.
     * @param integer $thetrys [optional] The number of attempts at resetting the
     *                initial parameters before giving up.
     * @return array A set of triples suitable for initializing a new Sudoku class
     */
    public function generatepuzzle($thedifficultylevel = 10, $themaxiterations = 50, $thetrys = 10) {
        $thedifficultylevel = min($thedifficultylevel, 10);
        $thedifficultylevel = max($thedifficultylevel, 1);

        $this->thelevel = 0;
        $this->thetrys = $thetrys;
        $this->themaxiterations = $themaxiterations;
        $this->thegenerationiterations = 0;

        for ($thetrys = 0; $thetrys < $this->thetrys; $thetrys++) {
            $theavailablepositions = [];
            $thecluespositions = [];
            $theclues = [];

            for ($i = 1; $i <= 9; $i++) {
                for ($j = 1; $j <= 9; $j++) {
                    $theavailablepositions[] = [$i, $j];
                }
            }

            $theinitialstate = $this->generatepuzzle($theavailablepositions, $thecluespositions, $theclues);

            if ($theinitialstate) {
                if ($thedifficultylevel != 10) {
                    $xxx = [];

                    foreach ($theinitialstate as $yyy) {
                        $xxx[] = (($yyy[0] - 1) * 9) + ($yyy[1] - 1);
                    }
                    /*
                     * Get rid of the available positions already used in the initial state.
                     */

                    sort($xxx);
                    $xxx = array_reverse($xxx);

                    foreach ($xxx as $i) {
                        array_splice($theavailablepositions, $i, 1);
                    }

                    /*
                     * Easy is defined as the number of derivable clues added to the minimum
                     * required information to solve the puzzle as returned by generatePuzzle.
                     */

                    for ($i = 0; $i < (10 - $thedifficultylevel); $i++) {
                        $xxx = mt_rand(0, count($theavailablepositions) - 1);
                        $row = $theavailablepositions[$xxx][0];
                        $col = $theavailablepositions[$xxx][1];
                        $theinitialstate[] = [$row, $col, $this->theboard[$row][$col]];
                        array_splice($theavailablepositions, $xxx, 1);
                    }
                }

                return $theinitialstate;
            }

            if ($this->thedebug) {
                printf("<br>Too many iterations (%d), %d\n", $this->themaxiterations, $thetrys);
            }
            $this->sudoku($this->thedebug);
        }

        /*
         * No solution possible, we guess wrong too many times.
         */

        return [];
    }

    /**
     * Get cel
     *
     * @param int $r
     * @param int $c
     */
    public function &getcell($r, $c) {
        return $this->theboard[$r][$c];
    }

    /**
     * Each element of the input array is a triple consisting of (row, column, value).
     * Each of these values is in the range 1..9.
     *
     * @param array $thearray
     */
    public function initializepuzzlefromarray($thearray) {
        foreach ($thearray as $xxx) {
            $c =& $this->getcell($xxx[0], $xxx[1]);
            $c->flagsolvedposition($xxx[2]);
        }
    }

    /**
     * predicate to determine if the current puzzle has been solved.
     *
     * @return boolean true if the puzzle has been solved.
     */
    public function issolved() {
        for ($i = 1; $i <= 9; $i++) {
            for ($j = 1; $j <= 9; $j++) {
                if (!$this->theboard[$i][$j]->issolved()) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Convert pending to actual solutions.
     *
     * This step is actually unnecessary unless you want a pretty output of the
     * intermediate.
     *
     * @return boolean True if at least on pending solution existed, false otherwise.
     */
    protected function newsolvedposition() {
        $thereturn = false;

        for ($i = 1; $i <= 9; $i++) {
            for ($j = 1; $j <= 9; $j++) {
                if ($this->theboard[$i][$j]->solvedstate() == 1) {
                    $this->theboard[$i][$j]->un_set($this->theboard[$i][$j]->getstate());
                    $thereturn = true;
                }
            }
        }

        return $thereturn;
    }

    /**
     * Print the contents of the board in HTML format.
     *
     * A "hook" so that extension classes can show all the steps taken by
     * the solve function.
     *
     * @param string $theheader [optional] The header line to be output along
     *               with the intermediate solution.
     */
    protected function printintermediatesolution($theheader = null) {
        if ($this->thedebug) {
            $this->printsolution($theheader);
        }
    }

    /**
     * Print the contents of the board in HTML format.
     *
     * Simple output, is tailored by hand so that an initial state and
     * a solution will find nicely upon a single 8.5 x 11 page of paper.
     *
     * @param mixed $theheader [optional] The header line[s] to be output along
     *               with the solution.
     */
    public function printsolution($theheader = null) {
        if (($this->thedebug) && ($theheader != null)) {
            if (is_array($theheader)) {
                foreach ($theheader as $aheader) {
                    print $aheader;
                }
            } else {
                print $theheader;
            }
        }

        $thecolors = ["green", "blue", "red"];
        $thefontsize = ["1em", "1em", ".8em"];
        $thefontweight = ["bold", "bold", "lighter"];

        printf("<br /><table border=\"1\" style=\"border-collapse: separate; border-spacing: 0px;\">\n");

        $thelast = 2;

        for ($i = 1; $i <= 9; $i++) {
            if ($thelast == 2) {
                printf("<tr>\n");
            }

            printf("<td><table border=\"1\" width=\"100%%\">\n");

            $thelast1 = 2;

            for ($j = 1; $j <= 9; $j++) {
                if ($thelast1 == 2) {
                    printf("<tr>\n");
                }

                $c = &$this->thesquares[$i];
                $c =& $c->getcell($j);
                $thesolvedstate = $c->solvedstate();

                printf(
                    "<td style=\"text-align: center; padding: .6em; color: %s; font-weight: %s; font-size: %s;\">",
                    $thecolors[$thesolvedstate],
                    $thefontweight[$thesolvedstate],
                    $thefontsize[$thesolvedstate]
                );
                $xxx = $c->asstring($this->thedebug);
                print ($xxx == " " ? "&nbsp;" : $xxx);
                printf("</td>\n");

                $thelast1 = ($j - 1) % 3;
                if ($thelast1 == 2) {
                    printf("</tr>\n");
                }
            }

            printf("</table></td>\n");

            $thelast = ($i - 1) % 3;
            if ($thelast == 2) {
                printf("</tr>\n");
            }
        }

        printf("</table>\n");
    }

    /**
     * Solve a Sudoku.
     *
     * As explained earlier, this works by iterating upon three different
     * types of inference:
     *
     * 1. A negative one, in which a value used within a row/column/square
     * may not appear elsewhere within the enclosing row/column/square.
     * 2. A positive one, in which any value with is unique in a row
     * or column or square must be the solution to that position.
     * 3. A tuple based positive one which comes in a number of flavors:
     * 3a. The "Pair" rule as stated by the author of the "other" Sudoku
     *     class on phpclasses.org and generalized by me, e.g., in any RCS
     *     two cells containing a pair of values eliminate those values from
     *     consideration in the rest of the RC or S.
     * 3b. The n/n+1 set rule as discovered by me, e.g., in any RCS, three cells
     *     containing the following pattern, (i, j)/(j, k)/(i, j, k) eliminate
     *     the values i, j, k from consideration in the rest of the RC or S.
     *
     * During processing I explain which structures (row, column, square)
     * are being used to infer solutions.
     *
     * @param boolean $theinitialstateflag [optional] True if the initial
     *                state of the board is to be printed upon entry, false
     *                otherwise.  [Default = true]
     * @return boolean true if a solution was possible, false otherwise.
     */
    public function solve($theinitialstateflag = true) {
        $theheader = "<br />Initial Position:";

        do {
            do {
                $this->applysolvedpositions();
                if ($theinitialstateflag) {
                    $this->printintermediatesolution($theheader);
                    $theheader = null;
                } else {
                    $theinitialstateflag = true;
                    $theheader = "<br />Apply Slice and Dice:";
                }
            } while ($this->newsolvedposition());

            $therowiteration = false;

            for ($i = 1; $i <= 9; $i++) {
                if ($this->theRows[$i]->doninference()) {
                    $theheader = $this->therows[$i]->getheader();
                    $therowiteration = true;
                    break;
                }
            }

            $thecolumniteration = false;

            if (!$therowiteration) {
                for ($i = 1; $i <= 9; $i++) {
                    if ($this->thecolumns[$i]->doaninference()) {
                        $theheader = $this->thecolumns[$i]->getheader();
                        $thecolumniteration = true;
                        break;
                    }
                }
            }

            $thesquareiteration = false;

            if (!($therowiteration || $thecolumniteration)) {
                for ($i = 1; $i <= 9; $i++) {
                    if ($this->thesquares[$i]->doaninference()) {
                        $theheader = $this->thesquares[$i]->getheader();
                        $thesquareiteration = true;
                        break;
                    }
                }
            }
        } while ($therowiteration || $thecolumniteration || $thesquareiteration);

        return $this->issolved();
    }
}
