//main entry function that calls the right //functions for getting sodoku answer func (inst *Solutionizer) GetSodokuSolution(board *sodoku.Board) string { inst.possibilities = 0 pass := inst.SetIndicesWithLeastPossibleChoices(board) if !pass { panic("Can't be solved!") return "" } return board.GetStringFormat() }
//for a particular empty index, returns the //numbers available that can poltentially be //inserted in that index func (inst *Solutionizer) getPossibilities(i, j int, board *sodoku.Board) []int { families := board.GetFamilies(i, j) availableNumbers := 987654321 for _, family := range families { availableNumbers = inst.availableNumbers(availableNumbers, family) } numsAvailable := inst.getPossibilitiesFromAvailableNumbers(availableNumbers) return numsAvailable }
//retrieves the family with the least //free options to choose from. For example //if a particular row only has one blank //indices then we know we can fill that entry //with 100% certainty(the unused number will go there) //returns true if solution was found func (inst *Solutionizer) SetIndicesWithLeastPossibleChoices(board *sodoku.Board) bool { toBeFilled := board.GetEmptyIndices() if len(toBeFilled) <= 0 && board.IsBoardComplete() { return true } oppertunityFound := false oppertunities := [9][]oppertunity{} boardChange := false for _, v := range toBeFilled { i, j := v[0], v[1] numAvailable := inst.getPossibilities(i, j, board) length := len(numAvailable) //if no oppertunities or certainties were found then //we are dealing with a faulty/broken board if length <= 0 { return false //if we only have one choice to choose from then we know 100% we can set it } else if length == 1 { //set indices for relatives board.SetEntry(i, j, numAvailable[0]) boardChange = true //other wise we track available oppurtunities thats ordered //based on amount of numbers available } else { oppertunityFound = true oppertunities[length] = append(oppertunities[length], oppertunity{i, j, numAvailable}) } } //if board was changed we call recursion on updated board if boardChange { return inst.SetIndicesWithLeastPossibleChoices(board) //else if at least one oppurtunity was found //we insert oppurtunity and recompute recursion //notice how oppertunities are traverse based on order //of minimum possibilities. This gives it a much higher chance //of success } else if oppertunityFound { //make copy of current entries before any alterations originalEntry := inst.copy(board.Entries) inst.possibilities += 1 for _, ops := range oppertunities { for _, op := range ops { if len(op.Entries) <= 0 { continue } for _, v := range op.Entries { board.SetEntry(op.I, op.J, v) pass := inst.SetIndicesWithLeastPossibleChoices(board) //if recursion returns true if pass { return true //otherwise if this oppertunity wasnt the best choice //we set it back to 0 and try next oppertunity } else { board.SetEntries(originalEntry) } } } } } return false }