UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: WordScramble 'new game' help please

Forums > 100 Days of SwiftUI

Ok so after several days of wracking my brain, I will admit to myself that I need help. My app seems to have some strange behaviour I can't quite figure out.

The code seems to work well until I call the 'restart' function using the button, to generate a new rootWord. The restart function clears the usedWords array, and clears most user scores (except 'high score'), then calls the 'startGame' function.

All looks well until you play the game. If you have entered say 3 or 4 words into the game (which populate the usedWords array), and then restart the game with a button press to generate the new rootWord, it seems that any new words submitted thereafter do not show in the list view...until you have entered a suffciient quantity of words which exceeds the previous array size. This is weird, as I have (I think) cleared the contents of the usedWords array, and new words are appended at 0. Further weirdness is that I can submit the same valid word repeatedly in the restarted games only, which seems to go through the 'guard' statements and correctly get added to the userScore, but they do NOT appear in my list view until, again, the word is submitted more times than the previous array's last size. Finally, I notice that repeatedly submittng the same word over and over (in a restarted game only) will increment the score correctly, but does not reject the repeated word which has already been submitted, until it starts to appear in my list on screen. It's like the words somehow aren't getting to the usedWords array, until I have submitted several words, but ONLY when playing a restarted game! I feel like I'm going mad.

I'm sure I am just being an idiot, but I cant figure out where this has gone wrong. Like I say, the game functions perfectly until I call restart to generate a new rootWord...so strange. Any help would be appreciated, but I'm also keen to understand how I can go through a debugging process to track my data and see what is 'inside' the array.

//
//  ContentView.swift
//  WordScramble
//
//  Created by David  on 19/4/2023.
//

import SwiftUI

struct ContentView: View {

    let backgroundGradient = LinearGradient(
        colors: [Color.red, Color.blue],
        startPoint: .top, endPoint: .bottom)

    @State private var usedWords = [String]()
    @State private var rootWord = ""
    @State private var newWord = ""

    @State private var errorTitle = ""
    @State private var errorMessage = ""
    @State private var showingError = false
    @State private var totalScore = 0
    @State private var wordCount = 0
    @State private var wordLengthScore = 0
    @State private var highScore = 0

    var body: some View {
        ZStack {
            NavigationView {
                List {
                    Section {
                        TextField("Enter your word", text: $newWord)
                            .autocapitalization(.none)
                    }
                    Section {
                        ForEach(usedWords, id: \.self) { word in
                            HStack {
                                Text(word)
                                Image(systemName: "\(word.count).circle.fill")
                            }
                        }
                    }
                }
                .background( .pink)
                .scrollContentBackground(.hidden)
                .navigationTitle(rootWord)
                .toolbar {
                    Button("New Word", action: restart)
                }
                .onSubmit(addNewWord)
                .onAppear(perform: startGame)
                .alert(errorTitle, isPresented: $showingError) {
                    Button("OK", role: .cancel) { }
                } message: {
                    Text(errorMessage)
                }
            }

            VStack {
                Spacer()
                    HStack{
                        Text("Word score: \(wordCount)")
                            .padding(20)
                        Text("Length Score: \(wordLengthScore)")
                }
                Text("Total Score: \(totalScore)")
                    .padding()
                Text("HIGH SCORE: \(highScore)")
            }
            .font(.headline)

        }

    }

    func addNewWord() {
        let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
        guard answer.count > 0 else { return }

        guard isOriginal(word: answer) else {
            wordError(title: "Word used already", message: "Be more original")
            return
        }

        guard isPossible(word: answer) else {
            wordError(title: "Word not possible", message: "You can't spell that word from '\(rootWord)'!")
            return
        }

        guard isReal(word: answer) else {
            wordError(title: "Word not recognized", message: "You can't just make them up, you know!")
            return
        }

        guard isNotRoot(word: answer) else {
            wordError(title: "Nice try", message: "That's the original word!")
            return
        }

        guard longEnough(word: answer) else {
            wordError(title: "Too short", message: "A word needs to be 3 or more letters!")
            return
        }
        wordCount += 1
        wordLengthScore += answer.count
        totalScore += wordLengthScore + wordCount
        if totalScore > highScore { highScore = totalScore } else { return }

        withAnimation {
            usedWords.insert(answer, at: 0)
        }
        newWord = ""
    }

    func startGame() {
        if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") {
            if let startWords = try? String(contentsOf: startWordsURL) {
                let allWords = startWords.components(separatedBy: "\n")
                rootWord = allWords.randomElement() ?? "silkworm"

                return
            }
        }
        fatalError("Fatal Error: Could not load start.txt from bundle")
    }
    func isOriginal(word: String) -> Bool {
        return !usedWords.contains(newWord)
    }

    func isPossible(word: String) -> Bool {
        var tempWord = rootWord
        for letter in word {
            if let pos = tempWord.firstIndex(of: letter) {
                tempWord.remove(at: pos)
            } else {
                return false
            }
        }
        return true
    }

    func isReal(word: String) -> Bool {
        let checker = UITextChecker()
        let range = NSRange(location: 0, length: word.utf16.count)
        let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")

        return misspelledRange.location == NSNotFound
    }

    func isNotRoot(word: String) -> Bool {
         return !(word == rootWord)
    }

    func longEnough(word: String) -> Bool {
        return word.count >= 3
    }

    func restart() {
        usedWords = []
        totalScore = 0
        wordCount = 0
        wordLengthScore = 0
        newWord = ""
        startGame()
    }

    func wordError(title: String, message: String) {
        errorTitle = title
        errorMessage = message
        showingError = true
    }

}
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }

2      

Hi, The problem is in the func addNewWord() you have an else return after the totalScore > highScore

if totalScore > highScore { highScore = totalScore } else { return }

it should be

if totalScore > highScore { highScore = totalScore }

3      

Wow!!! Amazing thank you so much Hector!

I was so focused on the functioning of the array, thinking i must be deleting the contents incorrectly or otherwise not handling the array properly I didn't spot that (obviously). You have no idea how grateful I am, thank you for saving my sanity!

edit: now I've seen it I can't believe I missed it...

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.