0

I want to create a list of levels with a score required to reach that level. But I'm not sure how best to do it.

For example I tried an enum like this:

    enum xpLevels {
    case 1 = 0...50
    case 2 = 51...100
    case 3 = 101...200
}

But this gives me errors saying: "Consecutive declarations on a line must be separated by ';'"

And I'm quite sure I haven't structured this right. Is an enum even the right thing to be using for this? I'd appreciate any help to point me in the right direction.

2
  • If I understand correctly, an array such as [50, 100, 200] will be enough to represent that. Why do you think enums will help? Commented Sep 24, 2020 at 5:56
  • Because if their score is 76 I'd want to return the Int: 2 as 76 is between the 51...100 needed for level 2 Commented Sep 24, 2020 at 5:58

3 Answers 3

1

If you really wanted enums, you might do something like this:

enum xpLevels {
    case low = 50
    case medium = 100
    case high = 200
}

Then, in your code, check if the value is <= low, else <= medium, else it must be "high" ;)

Sign up to request clarification or add additional context in comments.

1 Comment

I don't have any specific need to use an enum, I'm just seeking the best and most efficient way to do this. As I'll have up to 25 levels, each with an Int range required to get them. So I don't think I'd like to be using an if statement for each level type.
0

Enums are very unsuitable for this. Try an array instead. For example, you can store:

let levelXps = [0, 50, 100, 200]

Or if you want to write out the level numbers as well:

let levelXps = [0:0, 1:50, 2:100, 3:200].values.sorted()

Then you can have a function that tells you what level it is, given a score:

func level(forScore score: Int) -> Int? {
    levelXps.lastIndex(where: { score > $0 })?.advanced(by: 1)
}

This finds the index of the last item in the array that is smaller than the given score, then adds 1 to it. If the score is negative, the function returns nil.

6 Comments

Hi could you give a bit more detail on how to use this, I've tested this in playgrounds but no matter what score is given, it always just gives back an Int of 1
@dub Sorry, should have been lastIndex.
Thanks for this! Is there a way to do this visually instead of using an index to represent the level? I.e: levelXps = [1:100, 2:200, 3:300]
@dub That's called a dictionary. If you use a dictionary, you need to do a lot more work to get the level from the score. The numbers 1, 2, 3 etc are redundant data anyway. You don't actually need to store them.
@dub if you have lots of levels, and a list of numbers gets really hard to read, you can put a comment every 5 or 10 numbers indicating how many levels this is.
|
0

You can create an enumeration with the maximum score for each level as suggested by paulsm4 and declare your enumeration as CaseIterable. Then you can create an initializer to get the corresponding case for the score:

enum Level: Int, CaseIterable {
    case one = 50, two = 100, three = 200
}

extension Level {
    init(rawValue: Int) {
        self = Level.allCases.first { 0...$0.rawValue ~= rawValue } ?? .one
    }
}

extension Int {
    var level: Level { .init(rawValue: self) }
}

let score = 165
let level = score.level  // three


You can also create a custom enumeration of closed ranges. You just need to create a computed property to return each case range:

enum Level: CaseIterable {
    case one, two, three, four
}

extension Level: RawRepresentable {
    typealias RawValue = ClosedRange<Int>
    var rawValue: RawValue {
        let range: RawValue
        switch self {
        case .one:   range =   0 ... 50
        case .two:   range =  51 ... 100
        case .three: range = 101 ... 200
        case .four:  range = 201 ... .max
        }
        return range
    }
    init?(rawValue: RawValue) {
        guard let level = Level.allCases.first(where: { $0.rawValue == rawValue }) else { return nil }
        self = level
    }
    init(score: Int) {
        self = Level.allCases.first { $0.rawValue ~= score } ?? .one
    }
}


extension Int {
    var level: Level { .init(score: self) }
}

let score = 76
let level = score.level // two

7 Comments

This solution depends on the order returned by .allCases (which is, it seems, the order of cases in code). if written in ascending order of values, then it works, but if the order changes (e.g. three appearing in code before two), then it would give wrong results
Why would you mess with the levels order?
You typically wouldn't, but changing the order of cases in enum would not be expected to result in a change of code behavior, so it seems brittle to me
enumerations are static. There is no way for you to change their cases after defining them. This would affect an array as well. Btw an array can be changed if not declared as constant an enumeration can not.
Hmm... Unless I misunderstood you, that seems unrelated to my point. I mean that if some developer changed the order of cases enum A { case one, three, two }, then it would cause a hard-to-find bug because it would be unexpected (unlike changing the order in an array)
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.