Created
October 23, 2019 19:08
-
-
Save maartene/6021780809d38140ef18a210788d3e39 to your computer and use it in GitHub Desktop.
100 days of SwiftUI project 5 "Word Scamble" solution (including challenges)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// Word Scramble | |
// | |
// Created by Maarten Engels on 23/10/2019. | |
// Copyright © 2019 thedreamweb. All rights reserved. | |
// | |
import SwiftUI | |
struct ContentView: View { | |
@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 | |
var score: Int { | |
return usedWords.reduce(0) { result, word in | |
result + word.count * usedWords.count | |
} | |
} | |
var body: some View { | |
NavigationView { | |
VStack { | |
TextField("Enter your word", text: $newWord, onCommit: addNewWord) | |
.autocapitalization(.none) | |
.textFieldStyle(RoundedBorderTextFieldStyle()) | |
.padding() | |
List(usedWords, id: \.self) { | |
Image(systemName: "\($0.count).circle") | |
Text($0) | |
} | |
Text("Score: \(score) points").font(.largeTitle) | |
} | |
.navigationBarTitle(rootWord) | |
.navigationBarItems(trailing: | |
Button(action: startGame) { | |
Text("New word") | |
}) | |
.onAppear(perform: startGame) | |
.alert(isPresented: $showingError) { | |
Alert(title: Text(errorTitle), message: Text(errorMessage), dismissButton: .default(Text("OK"))) | |
} | |
} | |
} | |
func addNewWord() { | |
// lowercase and trim the word, to make sure we don't add duplicate words with case differences | |
let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) | |
// exit if the remaining string is empty | |
guard answer.count > 0 else { | |
return | |
} | |
guard answer != rootWord.lowercased() else { | |
wordError(title: "Cheeky!", message: "Be more original") | |
return | |
} | |
guard isLongEnough(word: answer) else { | |
wordError(title: "Too short!", message: "At least three letters...") | |
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 recognized", message: "You can't just make them up, you know!") | |
return | |
} | |
guard isReal(word: answer) else { | |
wordError(title: "Word not possible", message: "That isn't a real word.") | |
return | |
} | |
usedWords.insert(answer, at: 0) | |
newWord = "" | |
} | |
func startGame() { | |
// 0. Reset the used words | |
usedWords.removeAll() | |
// 1. Find the URL for start.txt in our app bundle | |
if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") { | |
// 2. Load start.txt into a string | |
if let startWords = try? String(contentsOf: startWordsURL) { | |
// 3. Split the string up into an array of strings, splitting on line breaks | |
let allWords = startWords.components(separatedBy: "\n") | |
// 4. Pick one random word, or use "silkworm" as a sensible default | |
rootWord = allWords.randomElement() ?? "silkworm" | |
// If we are here everything has worked, so we can exit | |
return | |
} | |
} | |
// If were are *here* then there was a problem – trigger a crash and report the error | |
fatalError("Could not load start.txt from bundle.") | |
} | |
func isOriginal(word: String) -> Bool { | |
!usedWords.contains(word) | |
} | |
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 wordError(title: String, message: String) { | |
errorTitle = title | |
errorMessage = message | |
showingError = true | |
} | |
func isLongEnough(word: String) -> Bool { | |
word.count >= 3 | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment