slots to hangman game

hello world ;) i am trying my best to learn swift and now I am trying to build a hangman game. so far I have made a word array to chose from and the buttons frem a-z and made it count the letters. but I kind a got stock trying to figure out how to generates the slots for each letter in the word. dose anyone have an idea, how I should approach this.

thanks.

Answered by ForumsContributor in

how to generates the slots for each letter in the word

What is the exact problem ? Is it to create the display or to put letters ?

What you could do:

  • create a label for each letter (you have to create as many as there are letters in word).

So the best is to create an IBOutletCollection

@IBOutlet var letters: [UILabel]!
  • Because the number of letters may vary, you may choose to create the labels in code:
var nbLetters : Int
// You should ask player to tell how many
for i in 0..<nbLetters {
  // create UILabels
   let label = UILabel(frame: CGRect(x: 20 * (i + 1), y: 50, width: 20, height: 21)) // You will set the x, y position as needed
   label.textAlignment = .center
   label.text = "_" // When there is no text
   letters.append(label)
   self.view.addSubview(label)
}
  • You could also decide in your game that words will be 10 letters max (for instance), and then create the 10 labels in storyboard, connect to the IBOutletCollection.
  • Then when you select a word, you will hide the non needed labels.

When you play, you put the letter where it fits (in the correct index of letters). For instance if T is in second (position 1 in array)

letters[1].text = "T"

Hope that will let you start. Start coding and come back if you have other problem.

Sorry, I just notice it is SwiftUI ? What I described was for UIKit.

In SwiftUI, you should create a Stack of Text for the letters.

Accepted Answer

Here's how I would do it ( maybe also with larger buttons and letter slots). Also, modify to allow different target words. Regards, Michaela

//  DataModel.swift
import Foundation
enum Letter : String {
  case a = "A", b = "B", c = "C", d = "D", e = "E", f = "F", g = "G", h = "H", i = "I", j = "J", k = "K", l = "L", m = "M", n = "N", o = "O", p = "P", q = "Q", r = "R", s = "S", t = "T", u = "U", v = "V", w = "W", x = "X", y = "Y", z = "Z", sp = "  "
}

struct WordLetter : Identifiable {
    // has to be identifiable because some words (e.g. HELLO) have non-unique letters: so can't use .self as id
    var id: Int = 0
    var letter: Letter = .sp
}

class DataModel : ObservableObject {
    let buttons: [[Letter]] = [
            [.a, .b, .c, .d, .e, .f, .g],
            [.h, .i, .j, .k, .l, .m, .n],
            [.o, .p, .q, .r, .s, .t, .u],
            [.v, .w, .x, .y, .z]]

    var word = "HELLO"
    @Published var finished = false {
        didSet {
            // now do something as a result of the user completing the word e.g. give a score, congratulate etc
        }
    }
    @Published var wordSlots = [WordLetter]() {
        didSet {
            // see if there are still any spaces, if so not finished
            for slot in wordSlots {
                if slot.letter == .sp {
                    return
                }
            }
            finished = true
        }
    }
    
    var pressedButton : Letter? {
        didSet {
            // see if a button press letter is part of the word
            findLetter()
        }
    }
   
    init() {
        // fill the Word Slots with blanks at the start - need to do this differently if you provide multiple target words
        var tempSlots = [WordLetter]()  // use a temporary array to save triggering an update to the main view for each blank added
        for i in (0..<word.count) {
            tempSlots.append(WordLetter(id: i, letter: .sp))
        }
        wordSlots = tempSlots
    }

    func findLetter() {
        for i in (0..<word.count) {
            let index = word.index(word.startIndex, offsetBy: i)
            guard let pressed = pressedButton else { return }
            // check if a pressed letter matches a letter (or more) in the word
            if String(word[index])  == pressed.rawValue {
                wordSlots[i].letter = pressed
            } 
        }
    }
}

import SwiftUI
struct ContentView: View {

    @EnvironmentObject var dataModel: DataModel

    var body: some View {
        VStack{
            Spacer()
            HStack{
                ForEach(dataModel.wordSlots) { wordLetter in
                    HStack{
                        Text(" ")
                        Text(wordLetter.letter.rawValue)
                        Text(" ")
                    } .border(.blue)
                }
            }    
            Spacer()
            ForEach(dataModel.buttons, id: \.self) {row in
                HStack{
                    ForEach(row, id: \.self) {item in
                        Button(action: {
                            dataModel.pressedButton = item }, label: {
                                    Text(item.rawValue) })
                    }
                }
            }
            Spacer()
        }
    }
}
slots to hangman game
 
 
Q