0

I am making Sudoku for iOS that will be simple project for my classes. The problem is the checking of the Sudoku. There can be three results form the checked Sudoku:

  1. Not filled
  2. Correct
  3. Incorect

Depending on enumeration with those values there will be a sheet that will display the result of the checked Sudoku.

Here is the enumeration:

// MARK: Statuses
/// Statuses that can result from checking the sudoku.
enum Statuses {
    case correct
    case incorrect
    case notFilled
}

Here is the struct that will hold the enumeration value and a bool variable that will be used to display the sheet.

// MARK: SudokuStatus
/// Struct that is used to combine the status and if the sudoku is checked.
struct SudokuStatus {
    public var status: Statuses
    public var isChecked: Bool
    
    /// Initialiser of the struct making the status not checked and empty.
    init () {
        self.status = .notFilled
        self.isChecked = false
    }
    
    /// Function that will update the status.
    /// - Parameter status: The status form the sudoku.
    /// - Returns: There is no return.
    mutating func setStatus(status: Statuses) -> Void {
        self.status = status
        self.isChecked = true
    }
}

Here is the content view.

// MARK: ContentView
/// The main view of the app.
struct ContentView: View {
    /// Variable that will create a sudoku object.
    let sudoku: Sudoku = Sudoku()
    /// Array that will hold the numbers in the sudoku.
    /// If the numbers is equals to 0 it means its empty.
    @State var numbers: [[Number]] = Array(repeating: Array(repeating: Number(), count: 9), count: 9)
    /// Variable that will keep the selected number.
    /// The value at begging will be 0 representing that no number is selected.
    @State var selectedNumber: Int = 0
    /// Variable used to selected the difficulty of the game.
    @State var selectedDifficulty: Difficulties = .easy
    /// Variable that will keep the status form the sudoku.
    @State var sudokuStatus: SudokuStatus = SudokuStatus()
    
    /// The main body of the struct.
    var body: some View {
        GeometryReader { geo in
            VStack(spacing: geo.size.height / 20) {
                Title (
                    width: geo.size.width / 1.2,
                    height: geo.size.height / 10
                )
                
                Board (
                    size: geo.size.width / 1.2,
                    selectedNumber: $selectedNumber,
                    numbers: $numbers
                )
                
                NumberSelection (
                    width: geo.size.width,
                    height: geo.size.height / 10,
                    selectedNumber: $selectedNumber
                )
                
                BottomBar (
                    width: geo.size.width,
                    height: geo.size.height / 10,
                    onStartGameClicked: onStartGameClicked,
                    onCheckGameClicked: onCheckGameClicked,
                    selectedDifficulty: $selectedDifficulty
                )
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .sheet(isPresented: $sudokuStatus.isChecked, content: {
                SudokuStatusSheet(status: sudokuStatus.status)
            })
        }
    }
    
    /// Function called when the start button is clicked.
    /// - Returns: There is no return. The function fills the array with the numbers.
    func onStartGameClicked() -> Void {
        let grid: [[Int]] = sudoku.generateSudoku(difficulty: selectedDifficulty)
        for i in 0..<9 {
            for j in 0..<9 {
                if grid[i][j] == 0 {
                    numbers[i][j].value = 0
                    numbers[i][j].isGenerated = false
                }
                else {
                    numbers[i][j].value = grid[i][j]
                    numbers[i][j].isGenerated = true
                }
            }
        }
    }
    
    /// Function called when the check button is clicked.
    /// - Returns: There is no return. The function updates the sudoku status variable.
    func onCheckGameClicked() -> Void {
        var grid: [[Int]] = Array(repeating: Array(repeating: 0, count: 9), count: 9)
        for i in 0..<9 {
            for j in 0..<9 {
                if numbers[i][j].value == 0 {
                    sudokuStatus.setStatus(status: .notFilled)
                    return
                }
                grid[i][j] = numbers[i][j].value
            }
        }
        
        if Sudoku.checkSudoku(grid: grid) {
            sudokuStatus.setStatus(status: .correct)
        } else {
            sudokuStatus.setStatus(status: .incorrect)
        }
    }
}

As you can see the function that check the Sudoku is very simple. It goes through the inputed numbers which a struct that hold the value of the number and bool variable to check if the number is generated or inputed by the user. If the number is equal to zero, which means empty, and it will update the value to not filled and return. Then it will do the check by the class Sudoku with work correctly and if the result is true it will make it correct to incorrect. The problem is that each time the app start or the preview is restarted the first time you check the Sudoku it will always says that it is not filled. After that the code works perfectly fine, but not the first time it checks.

I changed the value of the default initialiser of the struct Sudoku status and learned that the first value that will be displayed will the default that is set. I really have no idea how to solve that so I didn't try much more except searching the Internet with the same problem, or using our favourite AI, but it didn't help.

3
  • 3
    This needs a minimal reproducible example
    – jnpdx
    Commented Apr 24 at 20:04
  • Are you looking for @Observable ? Commented Apr 24 at 20:10
  • I'm not sure of the exact issue but you can't init objects in View structs like Sudoku because they will be lost, if it has no state and is just some funcs then make it a struct. Also it's sheet(item:) to show something
    – malhal
    Commented Apr 25 at 8:20

1 Answer 1

0

The best way to this is by creating a class that conforms to ObservableObject and has @Published values, as the following:

class SudokuStatus: ObservableObject {
    @Published var status: Statuses = .notFilled
    @Published var isChecked: Bool = false
    
    func setStatus(status: Statuses) {
        self.status = status
        self.isChecked = true
    }
}

You'll then have to declare this object as a @StateObject, and use it like you did, like this:

@StateObject var sudokuStatus: SudokuStatus = .init()

//...

            .sheet(isPresented: $sudokuStatus.isChecked) {
                SudokuStatusSheet(status: sudokuStatus.status)
            }

//...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.