Here I explain how to build a sudoku app with go-lang.
The aim of this application is to create an algorithm to fill in the cells of the puzzle with numbers, while ensuring rows and columns in a 3x3 grid contains numbers between 1 to 9 without repetition. And enable to the user to input values in different rows and columns, and check if this is correct within the 3x3 sub-grid as well as the whole grid.
Create a Backtracking Algorithm This fills the cells of the puzzle with numbers in each row, column and the sub-grid of 3x3, with digitals 1-9 that are no repeated within this sub-grid.
Create a function that takes in user’s input
Display puzzle to user in CLI and show progress
Create a Main function
package main
import (
"fmt"
"math/rand"
"time"
)
const (
boardSize = 9
boxSize = 3
)
type board [boardSize][boardSize]int
func (b *board) print() {
for i := 0; i < boardSize; i++ {
for j := 0; j < boardSize; j++ {
fmt.Printf("%d ", b[i][j])
}
fmt.Println()
}
}
func (b *board) isSafe(row, col, num int) bool {
// Check if the number is already in the same row, col or box
for x := 0; x < boardSize; x++ {
if b[row][x] == num || b[x][col] == num {
return false
}
}
// Check if the number is already in the same box
startRow, startCol := row-row%boxSize, col-col%boxSize
for i := 0; i < boxSize; i++ {
for j := 0; j < boxSize; j++ {
if b[i+startRow][j+startCol] == num {
return false
}
}
}
return true
}
func (b *board) generate() {
rand.Seed(time.Now().UnixNano())
for i := 0; i < boardSize; i++ {
for j := 0; j < boardSize; j++ {
num := rand.Intn(9) + 1
for !b.isSafe(i, j, num) {
num = rand.Intn(9) + 1
}
b[i][j] = num
}
}
}
func main() {
var b board
b.generate()
b.print()
}
Recently I asked Bard to provide me with a solution. In this solution I specified it to use Charm CLI packages where possible. Here’s the code it provided.
package main
import (
"fmt"
"github.com/charmbracelet/charm/cli"
"math/rand"
)
func main() {
app := cli.App("sudoku", "A CLI sudoku game")
app.Action(func(c *cli.Context) error {
board := generateBoard()
printBoard(board)
for {
fmt.Println("Enter the row and column of the cell you want to change:")
row, col, err := getRowCol(c)
if err != nil {
fmt.Println(err)
continue
}
fmt.Println("Enter the new value for the cell:")
value, err := getNewValue(c)
if err != nil {
fmt.Println(err)
continue
}
board[row][col] = value
printBoard(board)
}
return nil
})
app.Run(os.Args)
}
func generateBoard() [][]int {
board := make([][]int, 9)
for i := range board {
board[i] = make([]int, 9)
for j := range board[i] {
board[i][j] = rand.Intn(9) + 1
}
}
return board
}
func printBoard(board [][]int) {
for i := range board {
for j := range board[i] {
fmt.Printf("%d ", board[i][j])
}
fmt.Println()
}
}
func getRowCol(c *cli.Context) (int, int, error) {
rowStr := c.Args()[0]
colStr := c.Args()[1]
row, err := strconv.Atoi(rowStr)
if err != nil {
return -1, -1, fmt.Errorf("invalid row: %s", rowStr)
}
col, err := strconv.Atoi(colStr)
if err != nil {
return -1, -1, fmt.Errorf("invalid column: %s", colStr)
}
return row, col, nil
}
func getNewValue(c *cli.Context) (int, error) {
valueStr := c.Args()[2]
value, err := strconv.Atoi(valueStr)
if err != nil {
return -1, fmt.Errorf("invalid value: %s", valueStr)
}
return value, nil
}