Nonomino Sudoku

by codeonwort
♥1 | Line 224 | Modified 2012-11-15 00:27:08 | MIT License
play

ActionScript3 source code

/**
 * Copyright codeonwort ( http://wonderfl.net/user/codeonwort )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/9J1R
 */

package {
    import flash.text.TextFormat;
    import flash.text.TextField;
    import flash.display.Graphics;
    import flash.display.Sprite;
    
    public class NonominoSudokuTest extends Sprite {
        
        private var table:Array
        private var texts:Array = []
        private var canvas:Sprite
        
        private var colorMap:Array
        private var colorFunction:Array
        private var pieceMap:Vector.<Vector.<Cell>>
        
        private var validationLineX:Array, validationLineY:Array
        
        private var debug:TextField
        
        public function NonominoSudokuTest() {
            // write as3 code here..
            /*table = [
            [1,2,3,4,5,6,7,8,9],
            [7,8,9,1,2,3,4,5,6],
            [4,5,6,7,8,9,1,2,3],
            [3,1,2,6,4,5,9,7,8],
            [9,7,8,3,1,2,6,4,5],
            [6,4,5,9,7,8,3,1,2],
            [2,3,1,5,6,4,8,9,7],
            [8,9,7,2,3,1,5,6,4],
            [5,6,4,8,9,7,2,3,1]]*/
            colorMap = [
            [1,1,1,2,2,2,3,3,3],
            [1,1,1,2,2,2,3,3,3],
            [1,1,1,2,2,2,3,3,3],
            [4,4,4,5,5,5,6,6,6],
            [4,4,4,5,5,5,6,6,6],
            [4,4,4,5,5,5,6,6,6],
            [7,7,7,8,8,8,9,9,9],
            [7,7,7,8,8,8,9,9,9],
            [7,7,7,8,8,8,9,9,9]]
            colorFunction = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x00ffff, 0x0000ff, 0xff00ff, 0x8000ff, 0x0b173b]
            
            canvas = new Sprite
            canvas.x = canvas.y = 30
            addChild(canvas)
            
            addChild(debug = new TextField)
            debug.y = 10
            debug.autoSize = "left"
            debug.text = "debug: no log"
            
            generate()
            render(canvas, 40)
        }
        
        private function generate():void {
            var i:int, j:int, c:int
            
            validationLineX = []
            validationLineY = []
            
            for(i=0; i<100; i++){
                rotate(2 + Math.random() * 6, 2 + Math.random() * 6)
            }
            pieceMap = new Vector.<Vector.<Cell>>
            for(i=0; i<9; i++) pieceMap[i] = new Vector.<Cell>
            for(i=0; i<9; i++){
                for(j=0; j<9; j++){
                    c = colorMap[i][j] - 1
                    pieceMap[c].push(new Cell(i, j))
                }
            }
            solve()
        }
        
        private function render(canvas:Sprite, cellSize:Number):void {
            var g:Graphics = canvas.graphics
            g.clear()
            g.lineStyle(0, 0x0)
            for(var i:int=0; i<9; i++){
                for(var j:int=0; j<9; j++){
                    g.beginFill(colorFunction[colorMap[i][j]-1], 0.75)
                    g.drawRect(j*cellSize, i*cellSize, cellSize, cellSize)
                    g.endFill()
                }
            }
            
            g.lineStyle(4, 0x0)
            g.drawRect(0, 0, cellSize * 9, cellSize * 9)
            g.lineStyle()
            
            for each(var tf:TextField in texts){
                removeChild(tf)
            }
            for(i=0; i<9; i++){
                for(j=0; j<9; j++){
                    tf = new TextField
                    tf.autoSize = "left"
                    tf.defaultTextFormat = new TextFormat(null, 20)
                    tf.text = table[i][j] + ""
                    tf.x = j * cellSize + cellSize/2 - tf.width/2
                    tf.y = i * cellSize + cellSize/2 - tf.height/2
                    canvas.addChild(tf)
                    texts.push(tf)
                }
            }
        }
        
        // top-left corner: (0, 0)
        // bottom-right corner: (9, 9)
        // valid range: cx, cy ∈ [2, 7]
        private function rotate(cx:int, cy:int):void {
            if(cx < 2 || cx > 7) return
            if(cy < 2 || cy > 7) return
            var xlist:Array = [cx-2, cx-1, cx, cx+1, cx+1, cx+1, cx+1, cx, cx-1, cx-2, cx-2, cx-2]
            var ylist:Array = [cy-2, cy-2, cy-2, cy-2, cy-1, cy, cy+1, cy+1, cy+1, cy+1, cy, cy-1]
            var i:int, j:int, cand:Array = []
            for(i=0; i<9; i++) cand[i] = colorMap[i].concat()
            var first:int = cand[ylist[0]][xlist[0]]
            for(i=0; i<11; i++){
                cand[ylist[i]][xlist[i]] = cand[ylist[i+1]][xlist[i+1]]
            }
            cand[ylist[11]][xlist[11]] = first
            
            var valid:Boolean = true
            var visited:Array = []
            for(i=0; i<9; i++){
                visited[i] = []
                for(j=0; j<9; j++) visited[i][j] = false
            }
            var size:uint
            for(i=0; i<9; i++){
                for(j=0;j<9;j++){
                    if(visited[i][j] == false){
                        size = getPieceSize(i, j)
                        if(size != 9){
                            valid = false
                            break
                        }
                    }
                }
            }
            if(valid){
                colorMap = cand
            }
            function getPieceSize(i:int, j:int):uint {
                var n:uint = 1
                if(i<0 || i>=9 || j<0 || j>=9 || visited[i][j]) return 0
                visited[i][j] = true
                if(i>0 && cand[i][j] == cand[i-1][j]) n += getPieceSize(i-1, j)
                if(i<8 && cand[i][j] == cand[i+1][j]) n += getPieceSize(i+1, j)
                if(j>0 && cand[i][j] == cand[i][j-1]) n += getPieceSize(i, j-1)
                if(j<8 && cand[i][j] == cand[i][j+1]) n += getPieceSize(i, j+1)
                return n
            }
        }
        
        private function solve():void {
            var i:int, j:int
            
            // clear the table
            table = []
            for(i=0; i<9; i++){
                table[i] = []
                for(j=0; j<9; j++){
                    table[i][j] = 0
                }
            }
            
            // fill one color area
            var flood_count:int = 1
            floodFill(0, 0)
            function floodFill(i:int, j:int):void {
                table[i][j] = flood_count
                flood_count += 1
                if(i>0 && table[i-1][j] == 0 && colorMap[i][j] == colorMap[i-1][j]) floodFill(i-1, j)
                if(i<8 && table[i+1][j] == 0 && colorMap[i][j] == colorMap[i+1][j]) floodFill(i+1, j)
                if(j>0 && table[i][j-1] == 0 && colorMap[i][j] == colorMap[i][j-1]) floodFill(i, j-1)
                if(j<8 && table[i][j+1] == 0 && colorMap[i][j] == colorMap[i][j+1]) floodFill(i, j+1)
            }
            
            // backtracking
            backtrack()
        }
        
        private function backtrack():void {
            var cells:Vector.<Cell> = getEmptyCells()
            var cell:Cell
            var idx:int = 0, cellValue:int
            var solutionFound:Boolean
            
            var cnt:int = 0, maxIdx:int=0
            
            while(true){
                cell = cells[idx]
                if(table[cell.i][cell.j] >= 9){
                    // have to backtrack
                    table[cell.i][cell.j] = 0
                    idx --
                    if(idx == -1){
                        // no solution
                        solutionFound = false
                        break
                    }
                    continue
                }
                table[cell.i][cell.j] += 1
                if(validSoFar(cell.i, cell.j)){
                    // go next cell
                    idx++
                    if(idx > maxIdx) maxIdx = idx
                    if(idx == cells.length){
                        solutionFound = true
                        break
                    }
                }
                cnt ++
            }
            //debug.text = "loop iter: " + cnt + "      max idx: " + maxIdx
        }
        
        private function validSoFar(candI:int, candJ:int):Boolean {
            var i:int, color:int, num:int
            var piece:Vector.<Cell>
            
            color = colorMap[candI][candJ] - 1
            piece = pieceMap[color]
            for(i=0; i<9; i++) validationLineX[i] = false
            for(i=0; i<9; i++){
                num = table[piece[i].i][piece[i].j] - 1
                if(num == -1) continue
                if(validationLineX[num]) return false
                validationLineX[num] = true
            }
            // check cross line centered at (candX, candY)
            for(i=0; i<9; i++) validationLineX[i] = validationLineY[i] = false
            for(i=0; i<9; i++){
                num = table[candI][i] - 1
                if(num == -1) continue
                if(validationLineX[num]) return false
                validationLineX[num] = true
            }
            for(i=0; i<9; i++){
                num = table[i][candJ] - 1
                if(num == -1) continue
                if(validationLineY[num]) return false
                validationLineY[num] = true
            }
            return true
            /* do not check all cells, just cells related with current candidate cell.
            // check pieces
            var color:int, num:int
            for(var i:int=0; i<9; i++){
                for(var j:int=0; j<9; j++){
                    validationTable[i][j] = false
                }
            }
            for(i=0; i<9; i++){
                for(j=0; j<9; j++){
                    color = colorMap[i][j] - 1;
                    num = table[i][j] - 1;
                    if(num == -1) continue
                    if(validationTable[color][num]) return false;
                    validationTable[color][num] = true;
                }
            }
            // check lines
            for(i=0; i<9; i++){
                for(j=0; j<9; j++) validationLineX[j] = validationLineY[j] = false;
                for(j=0; j<9; j++){
                    num = table[i][j] - 1;
                    if(num == -1) continue
                    if(validationLineX[num]) return false;
                    validationLineX[num] = true;
                }
                for(j=0; j<9; j++){
                    num = table[j][i] - 1;
                    if(num == -1) continue
                    if(validationLineY[num]) return false;
                    validationLineY[num] = true;
                }
            }
            return true;
            */
        }
                
        private function getEmptyCells():Vector.<Cell> {
            var cells:Vector.<Cell> = new Vector.<Cell>
            for(var i:int=0; i<9; i++){
                for(var j:int=0; j<9; j++){
                    if(table[i][j] == 0){
                        cells.push(new Cell(i, j))
                    }
                }
            }
            return cells
        }
        
    }
    
}

class Cell {
    public var i:int, j:int
    public function Cell(ic:int, jc:int) {
        i = ic
        j = jc
    }
}

Forked