Sudoku solver in Groovy





11
Date Submitted Fri. Oct. 20th, 2006 7:02 AM
Revision 1 of 1
Helper glaforge
Tags groovy
Comments 0 comments
Sudoku solver in Groovy.

#!/usr/bin/env groovy

/**
 * Sudoku solver
 */


// the games to solve
def games = [
    "   1    5  7 48  2 2 9    7 3   29  5       4  68   1 8    1 4 6  28 5  1    4   ",
    "   897     9     1  6 1  9  3     2    574    1     6  8  2 7  5     9     763   ",
    "5   1 9  73 2    5    6  7    5  8  8       3  4  7    1  8    2    1 69  6 7   4",
    " 7  4  63  2  9 4 5     8      7 3  9  8 6  7  8 5      7     2 1 4  7  69  2  3 ",
    "57  9 18  3      4 8    6    24 5               6 95    5    9 3      2  91 3  75",
    " 7  4  63  2  9 4 5     8      7 3  9  8 6  7  8 5      7     2 1 4  7  69  2  3 ",
    "18    4     8       9 345   4 96    52  8  76    53 1   251 7       2     7    92",
    " 6  3     459   28  8   73     9  5 9  8 6  7 8  5     36   9  42   938     2  1 ",
    "  14         786 1    5 9   8     23 13   56 95     7   5 4    3 918         73  ",
    "7 5 2   3  35     4  7    6    3 82           62 9    3    8  9     41  1   7 3 2",
    "  1  74      2  966  3      57  89  93     51  69  27      6  582  7      52  8  ",
    "  73  2  3       18  62     734    5         5    849     67  42       6  9  43  "
]

def solver = new Backtracker()

solver.isSolution = { theGame ->
    def numbersSolved = 0
    for (i in 0..<9) {
        for (j in 0..<9) {
            if (theGame.grid[i][j].value) numbersSolved++
        }
    }
    return numbersSolved == 81
}

solver.possibilities = { theGame ->

    playSingleCandidates(theGame)
   
    if (solver.isSolution(theGame)) {
        return [theGame]
    } else {
        def position = getPositionWithLowestNumberOfCandidates(theGame)
        def candidates = theGame.grid[position[0]][position[1]].candidates
        return candidates.collect{
            def possibleGrid = new Grid(theGame)
            possibleGrid.play(it, position[0], position[1])
            return possibleGrid
        }       
    }
}

Grid playSingleCandidates(Grid game) {
    def player = {
        def counter = 0     
        for (i in 0..<9) {
            for (j in 0..<9) {
                if (game.grid[i][j].candidates.size() == 1) {
                    game.play(game.grid[i][j].candidates[0], i, j)
                    counter++
                }
            }
        }   
        return counter
    }   
    while (true) {
        if (player() == 0) break
    }
}

List getPositionWithLowestNumberOfCandidates(Grid game) {
    def position = [0,0]
    def lowestNumberOfCandidates = 9
    for (i in 0..<9) {
        for (j in 0..<9) {
            def cell = game.grid[i][j]
            if (cell.value == null && cell.candidates.size() < lowestNumberOfCandidates) {
                position = [i,j]
                lowestNumberOfCandidates = cell.candidates.size()
            }
        }
    }
    return position
}

games.each{ game ->
    def grid = new Grid()
    grid.loadGame(game)

    def start = System.currentTimeMillis()
    def solution = solver.solve(grid)

    if (solution)
        println solution.toString()
    else
        println "No solution found"

    println "\nSolved in ${System.currentTimeMillis() - start}ms with ${solver.stages} steps and ${solver.backtracks} backtracks"
}

class Cell implements Cloneable {
    def value
    def candidates = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
   
    Object clone() {
        def newCell = new Cell()
        newCell.value = value
        newCell.candidates = candidates.clone()
        return newCell
    }
}

class Grid {
    def grid = [[], [], [], [], [], [], [], [], []]
   
    Grid() {
        for(i in 0..<9)
            for (j in 0..<9)
                grid[i][j] = new Cell()
    }
   
    Grid(Grid g) {
        for(i in 0..<9)
            for (j in 0..<9)
                grid[i][j] = g.grid[i][j].clone()       
    }
   
    void loadGame(String game) {
        def counter = 0
        for(c in game) {
            if (c ==~ "[1-9]")
                play(c, counter.intdiv(9), counter % 9)
            counter++
        }
    }
   
    void play(number, line, column) {
        def cell = grid[line][column]
        cell.value = number
        cell.candidates = []
        removeCandidateFromLine([number], line)
        removeCandidateFromColumn([number], column)
        removeCandidateFromBlock([number], line, column)
    }
   
    private void removeCandidateFromLine(candidates, line) {
        for (i in 0..<9)
            grid[line][i].candidates = grid[line][i].candidates - candidates
    }
   
    private void removeCandidateFromColumn(candidates, column) {
        for (i in 0..<9)
            grid[i][column].candidates = grid[i][column].candidates - candidates
    }
   
    private void removeCandidateFromBlock(candidates, line, column) {
        for (i in 0..<3)
            for (j in 0..<3) {
                def x = i + 3*(line.intdiv(3))
                def y = j + 3*(column.intdiv(3))
                grid[x][y].candidates = grid[x][y].candidates - candidates
            }
    }
   
    String candidatesToString() {
        def sb = new StringBuffer()
        for(i in 0..<9) {
            sb << (i % 3 == 0 ? "\n+" + ("-" * 27 + "+") * 3 : "") + "\n"
            for (j in 0..<9) {
                if (j % 3 == 0) sb << "|"
                def cell = grid[i][j]
                sb << cell.candidates.join('').center(9)
            }
            sb << "|"
        }
        sb << "\n+" + ("-" * 27 + "+") * 3 + "\n"
        return sb.toString()
    }

    String toString() {
        def sb = new StringBuffer()
        for(i in 0..<9) {
            sb << (i % 3 == 0 ? "\n+---+---+---+\n" : "\n")
            for (j in 0..<9) {
                if (j % 3 == 0) sb << "|"
                def cell = grid[i][j]
                sb << (cell.value ? cell.value : " ")
            }
            sb << "|"
        }
        sb << "\n+---+---+---+\n"
        return sb.toString()
    }
   
    boolean equals(Object obj) {
        this.hashCode() == obj?.hashCode()     
    }
   
    int hashCode() {
        def sb = new StringBuffer()
        for(i in 0..<9)
            for (j in 0..<9)
                sb << (grid[i][j].candidates + grid[i][j].value)
        return sb.toString().hashCode()
    }
}

class Backtracker {
    def possibilities
    def isSolution
    def stages = 0
    def backtracks = 0
 
    def solve(possibility) {
        def steps = []
        doSolveRecurse(steps, possibility)
    }
 
    private doSolveRecurse(List steps, currentPossibility) {
        if (isSolution(currentPossibility)) {
            return currentPossibility
        } else {
            stages++
            def allPossibilities = possibilities(currentPossibility)
            if (allPossibilities.size() > 0) {
                steps << currentPossibility
            } else {
                // we've backtracked all the way to the root, so there's no solution
                if (steps.size() <= 1) return null
                def popped = steps.pop()
                backtracks++
            }
            for(possibility in allPossibilities) {
                def value = doSolveRecurse(steps, possibility)
                if (value != null) return value
            }           
        }
    }   
}
 

Guillaume Laforge

glaforge.free.fr/blog/groovy

Comments

There are currently no comments for this snippet.

Voting