Package com.barrybecker4.puzzle.sudoku

Source Code of com.barrybecker4.puzzle.sudoku.SudokuGenerator

/** Copyright by Barry G. Becker, 2000-2011. Licensed under MIT License: http://www.opensource.org/licenses/MIT  */
package com.barrybecker4.puzzle.sudoku;

import com.barrybecker4.common.concurrency.ThreadUtil;
import com.barrybecker4.common.math.MathUtil;
import com.barrybecker4.puzzle.sudoku.model.board.Board;
import com.barrybecker4.puzzle.sudoku.model.board.Cell;
import com.barrybecker4.puzzle.sudoku.model.board.ValuesList;
import com.barrybecker4.puzzle.sudoku.ui.SudokuPanel;

import java.util.Collections;
import java.util.List;

/**
* Generate a Sudoku puzzle.
* Initially created with grandma Becker on Date: Jul 8, 2006
*
* @author Barry Becker
*/
public class SudokuGenerator {

    private int size_;
    private int delay_;
    private SudokuPanel ppanel_;
    private long totalCt;

    /**
     * Use this Constructor if you do not need to show the board in a UI.
     * @param baseSize 4, 9, or 16
     */
    public SudokuGenerator(int baseSize) {
        this(baseSize, null);
    }
    /**
     * Constructor
     * @param baseSize 4, 9, or 16
     * @param ppanel renders the puzzle. May be null if you do not want to see animation.
     */
    public SudokuGenerator(int baseSize, SudokuPanel ppanel) {
        size_ = baseSize;
        ppanel_ = ppanel;
        totalCt = 0;
    }

    public void setDelay(int delay) {
        delay_ = delay;
    }

    /**
     * find a complete consistent solution.
     * @return generated random board
     */
    public Board generatePuzzleBoard() {

        Board board = new Board(size_);

        if (ppanel_ != null)  {
            ppanel_.setBoard(board);
        }

        boolean success = generateSolution(board);
        if (ppanel_ != null) ppanel_.repaint();

        assert success : "We were not able to generate a consistent board "+ board + "numCombinations examined = " + totalCt;

        // now start removing values until we cannot deduce the final solution from it.
        // for every position (in random order) if we can remove it, do so.
        return generateByRemoving(board);
    }

    protected boolean generateSolution(Board board) {
        return generateSolution(board, 0);
    }


    /**
     * Recursive method to generate a completely solved, consistent sudoku board.
     * If at any point we find that we have an inconsistent/unsolvable board, then backtrack.
     * @param board the currently generated board (may be partial)
     * @return whether or not the current board is consistent.
     */
    protected boolean generateSolution(Board board, int position) {

        // base case of the recursion
        if (position == board.getNumCells())  {
            // board completely solved now
            return true;
        }

        Cell cell = board.getCell(position);
        ValuesList shuffledValues = ValuesList.getShuffledCandidates(cell.getCandidates());

        refresh();

        for (int value : shuffledValues) {

            cell.setValue(value);
            totalCt++;
            if (generateSolution(board, position + 1)) {
                return true;
            }
            cell.clearValue();
        }

        return false;
    }

    private void refresh() {
        if (ppanel_ == null) return;

        if (delay_ >=0 )  {
            ppanel_.repaint();
            ThreadUtil.sleep(delay_);
        }
    }

    /**
     * Generate a sudoku puzzle that someone can solve.
     * @param board the initially solved puzzle
     * @return same puzzle after removing values in as many cells as possible and still retain consistency.
     */
    private Board generateByRemoving(Board board) {

        if (ppanel_ != null) {
            ppanel_.setBoard(board);
        }

        List positionList = getRandomPositions(size_);
        // we need a solver to verify that we can still deduce the original
        SudokuSolver solver = new SudokuSolver();
        solver.setDelay(delay_);

        int len = size_ * size_;
        int last = len * len;
        // the first len can be removed without worrying about having an unsolvable puzzle.
        for (int i=0; i < len; i++) {
            int pos = (Integer) positionList.get(i) - 1;
            board.getCell(pos).clearValue();
        }

        for (int i=len; i < last; i++) {
            int pos = (Integer) positionList.get(i) - 1;
            tryRemovingValue(pos, board, solver);
        }

        return board;
    }

    /**
     * @param pos  position to try removing.
     */
    private void tryRemovingValue(int pos, Board board, SudokuSolver solver) {
        Cell cell = board.getCell(pos);
        int value = cell.getValue();
        cell.clearValue();

        if (ppanel_ != null && delay_ > 0) {
            ppanel_.repaint();
        }

        Board copy = new Board(board)// try to avoid this
        if (!solver.solvePuzzle(copy, ppanel_)) {
            // put it back since it cannot be solved without this positions value
            cell.setOriginalValue(value);
        }
    }

    /**
     * @param size the base size (fourth root of the number of cells).
     * @return the positions on the board in a random order in a list .
     */
    private ValuesList getRandomPositions(int size) {
        int numPositions = size * size * size * size;
        ValuesList positionList = new ValuesList(numPositions);
        Collections.shuffle(positionList, MathUtil.RANDOM);
        return positionList;
    }

}
TOP

Related Classes of com.barrybecker4.puzzle.sudoku.SudokuGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.