diff --git a/sudoku/sudoku.lua b/sudoku/sudoku.lua
index 344d2bf..c8ba73f 100644
--- a/sudoku/sudoku.lua
+++ b/sudoku/sudoku.lua
@@ -527,6 +527,15 @@ function sHelper(n)
     solve(board)
 end
 
+function checkBoardsEqual(b1, b2)
+    for i = 1, 9 do
+        for j = 1, 9 do
+            if b1[i][j] ~= b2[i][j] then return false end
+        end
+    end
+    return true
+end
+
 -- b = loadBoard("b.txt")[1]
 -- applyFillSinglesRepeatedly(b)
 -- s = buildSearchSpace(b)
@@ -535,8 +544,11 @@ end
 
 return {
     loadBoard = loadBoard,
-    calCellValidOptions = calCellValidOptions,
     cloneBoard = cloneBoard,
-    checkBoard = checkBoard,
     createEmptyBoard = createEmptyBoard,
+    calCellValidOptions = calCellValidOptions,
+    checkBoard = checkBoard,
+    applyFillSinglesRepeatedly = applyFillSinglesRepeatedly,
+    showBoard = showBoard,
+    checkBoardsEqual = checkBoardsEqual, 
 }
diff --git a/ui/sudokucanvas.lua b/ui/sudokucanvas.lua
index db78c37..1d5a567 100644
--- a/ui/sudokucanvas.lua
+++ b/ui/sudokucanvas.lua
@@ -1,6 +1,47 @@
 SudokuCanvas = {}
 SudokuCanvas.__index = SudokuCanvas
 
+function SudokuCanvas:pushHistory()
+    if #self.history <= 1 then
+        table.insert(self.history, sudoku.cloneBoard(self.board[2]))
+        -- showHistory(self)
+        -- print("pushHistory: ", #self.history)
+    elseif not sudoku.checkBoardsEqual(self.board[2], self.history[#self.history]) then
+        table.insert(self.history, sudoku.cloneBoard(self.board[2]))
+        -- showHistory(self)
+        -- print("pushHistory: ", #self.history)
+    end
+end
+
+function showHistory(self)
+    for i, v in ipairs(self.history) do
+        print(string.format("-- [%03d] ----------", i))
+        sudoku.showBoard(v)
+    end
+end
+
+function undo(self)
+    if #self.history > 1 then
+        showHistory(self)
+        table.remove(self.history)
+        self.board[2] = sudoku.cloneBoard(self.history[#self.history])
+        self.board[3] = sudoku.cloneBoard(self.board[2])
+
+        -- self.board[2] = sudoku.cloneBoard(self.history[#self.history])
+        -- self.history[#self.history] = nil
+
+        self:checkBoard(true)
+        print"-------------------------------------------"
+        showHistory(self)
+    end
+end
+
+function applyAllSingles(self)
+    sudoku.applyFillSinglesRepeatedly(self.board[2])
+    self.board[3] = sudoku.cloneBoard(self.board[2])
+    self:checkBoard()
+end
+
 function showValidOptionsForCurrentCell(self, x, y)
     local s = sudoku.calCellValidOptions(self.board[2], y, x)
     for num = 1, 9 do
@@ -211,6 +252,7 @@ function SudokuCanvas:new(x, y, size, options)
         colors = options.colors,
         enabled = true,
         focused = true,
+        history = {},
     }
     o.grid = buildGrid(x, y, cellSize)
     o.smallNumbers = calSmallNumbersCoordinates(o.grid)
@@ -228,7 +270,8 @@ end
 function SudokuCanvas:focus()
 end
 
-function SudokuCanvas:checkBoard()
+function SudokuCanvas:checkBoard(hist)
+    if not hist then self:pushHistory() end
     local ok, isComplete, conflictList = sudoku.checkBoard(self.board[2])
     self.board[4] = sudoku.createEmptyBoard()
     for _, c in ipairs(conflictList) do
@@ -342,7 +385,7 @@ function SudokuCanvas:keypressed(key)
                     self.board[2][y][x] = num
                     self.board[3][y][x] = num
                 end
-                self:checkBoard()
+                -- self:checkBoard()
             else
                 self.smallNumbers[x][y][num].enabled = not self.smallNumbers[x][y][num].enabled
             end
@@ -382,9 +425,12 @@ function SudokuCanvas:keypressed(key)
             mode = "big"
         end
     elseif key == "s" then
-        showValidOptionsForCurrentCell(self, x, y)
-    elseif key == "a" then
+        -- showValidOptionsForCurrentCell(self, x, y)
         showValidOptionsForAllCells(self)
+    elseif key == "a" then
+        applyAllSingles(self)
+    elseif key == "u" then
+        undo(self)
     end
     self.cursor.x = x
     self.cursor.y = y