changeset 25:af24a1a7194b default tip

sudoku.py
author Jordi Gutiérrez Hermoso <jordigh@octave.org>
date Mon, 22 May 2017 11:30:19 -0400
parents 7bcb491ce57e
children
files haskell/levenshtein/fuco.hs python/sudoku.py
diffstat 2 files changed, 184 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
old mode 100644
new mode 100755
--- a/haskell/levenshtein/fuco.hs
+++ b/haskell/levenshtein/fuco.hs
@@ -1,13 +1,16 @@
-nthRow n len = n : replicate (len - 1) 0
+#!/usr/bin/runhaskell
 
-updateRow prev n letter word 
-  = reverse $ foldl (\l@(last:_) (j1,j,cl) 
+import Data.List
+
+updateRow prev n letter word = 
+  reverse $ foldl' (\l@(last:_) (j1,j,cl) 
                      -> minimum [1 + last, 1 + j, 
                                  j1 + (if letter == cl then 0 else 1)] : l) [n] p
   where
     p = zip3 prev (tail prev) word
 
-leven a b = last $ foldl (\x(n,letter) 
-                          -> updateRow x n letter b) [0..(length b)] (zip [0..] a)
+leven a b = last $ foldl'
+            (\x(n,letter) -> updateRow x n letter b) 
+            [0..(length b)] (zip [0..] a)
 
-main = print $ leven (show [1..1000]) (show [2..1001])
\ No newline at end of file
+main = print $ leven (show [1..300]) (show [2..301])
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/python/sudoku.py
@@ -0,0 +1,175 @@
+from copy import deepcopy
+
+
+class InvalidMove(Exception):
+    pass
+
+
+class InvalidBoard(Exception):
+    pass
+
+
+class InvalidEntry(Exception):
+    pass
+
+
+def possibles():
+    return set(range(1, 10))
+
+
+class Board(object):
+    def __init__(self, board=None):
+        self.rows = []
+        self.cols = []
+        for i in range(0, 9):
+            self.rows.append(possibles())
+            self.cols.append(possibles())
+
+        self.subboards = []
+        for i in range(0, 3):
+            self.subboards.append([])
+            for j in range(0, 3):
+                self.subboards[-1].append(possibles())
+
+        self.board = []
+        for i in range(0, 9):
+            self.board.append(["."]*9)
+
+        if board is not None:
+            self.read(board)
+
+    def __repr__(self):
+        return """
+        +---+---+---+
+        |{}{}{}|{}{}{}|{}{}{}|
+        |{}{}{}|{}{}{}|{}{}{}|
+        |{}{}{}|{}{}{}|{}{}{}|
+        +---+---+---+
+        |{}{}{}|{}{}{}|{}{}{}|
+        |{}{}{}|{}{}{}|{}{}{}|
+        |{}{}{}|{}{}{}|{}{}{}|
+        +---+---+---+
+        |{}{}{}|{}{}{}|{}{}{}|
+        |{}{}{}|{}{}{}|{}{}{}|
+        |{}{}{}|{}{}{}|{}{}{}|
+        +---+---+---+""".format(*sum(self.board, []))
+
+    def is_solved(self):
+        return all(["." not in row for row in self.board])
+
+    def read(self, board):
+        i = 0
+        j = 0
+        while i < 9 and j < 9:
+            for entry in board:
+                if entry == ".":
+                    j += 1
+                elif entry in "123456789":
+                    self.insert(i, j, int(entry))
+                    j += 1
+
+                if j == 9:
+                    j = 0
+                    i += 1
+
+    def get_possibles(self, i, j):
+        if self.board[i][j] != ".":
+            return {self.board[i][j]}
+        return (
+            self.rows[i] &
+            self.cols[j] &
+            self.subboards[i//3][j//3]
+        )
+
+    def show_all_possibles(self):
+        for i in range(0, 9):
+            for j in range(0, 9):
+                print self.get_possibles(i, j)
+            print
+
+    def insert(self, i, j, val):
+        if self.board[i][j] != ".":
+            raise InvalidEntry()
+
+        if val not in self.get_possibles(i, j):
+            raise InvalidMove()
+
+        self.board[i][j] = val
+        self.rows[i].remove(val)
+        self.cols[j].remove(val)
+        self.subboards[i//3][j//3].remove(val)
+
+    def reduce(self):
+        reduced = True
+        while reduced:
+            reduced = False
+            for i in range(0, 9):
+                for j in range(0, 9):
+                    if self.board[i][j] == ".":
+                        possibles = self.get_possibles(i, j)
+                        if len(possibles) == 1:
+                            try:
+                                self.insert(i, j, possibles.pop())
+                            except InvalidEntry:
+                                raise InvalidBoard()
+                            reduced = True
+
+        return self
+
+    def all_feasibles(self):
+        all_feasibles = []
+        for i in range(0, 9):
+            for j in range(0, 9):
+                feasibles = self.get_possibles(i, j)
+                if len(feasibles) > 1:
+                    all_feasibles.append([i, j, feasibles])
+                if len(feasibles) == 0:
+                    raise InvalidBoard()
+
+        all_feasibles = sorted(
+            all_feasibles,
+            key=lambda x: len(x[2])
+        )
+
+        if len(all_feasibles) == 0:
+            raise InvalidBoard()
+
+        return all_feasibles
+
+
+    def solve(self):
+        self.reduce()
+        while not self.is_solved():
+            all_feasibles = self.all_feasibles()
+            try_i, try_j, feasibles = all_feasibles[0]
+            for feasible in feasibles:
+                attempt = deepcopy(self)
+                attempt.insert(try_i, try_j, feasible)
+                try:
+                    self = attempt.solve()
+                except InvalidBoard:
+                    continue
+
+                # I.e. not an invalid board, it's solved
+                break
+            else:
+                raise InvalidBoard
+
+        return self
+
+if __name__=="__main__":
+    b = Board(board="""
+    ... .5. .74
+    8.1 ... ...
+    ... ... ...
+    
+    .7. 24. ...
+    6.. ... 1..
+    ... ... ...
+
+    2.. 1.6 3..
+    .4. ... .2.
+    ... 8.. ...
+
+    """)
+    print b.solve()