45

I am making a board game where you must joins two objects. Each object has a type and there are 5 different types. For each different type combination in a merge, there will be a different effect on the game. Right now, I am working with a switch statement for each combination. So, my code would look something like this.

struct Coin {
    var type = 1
}

// Each coin is of type Coin.   
switch(coin1.type, coin2.type) {
    case (1,1):
        functionNormal()
    case (1,2):
        functionUncommon()
    case (1,3):
        functionRare()
    ...
}

The objects' position doesn't change the result. A (1,2) merge will have the same effect as a (2,1) merge. Is there a less verbose way to achieve this?

Alain Cruz
  • 4,757
  • 3
  • 25
  • 43
  • It may be a bit kludgy, but you could "sort" the raw values of the two objects (or something like that) such that `case (2,1)` would never happen. –  Mar 28 '17 at 00:48
  • I know it's just an example, but never name a `struct` `Object`. – Alexander Mar 28 '17 at 00:51

4 Answers4

88

You can pass multiple comma-separated cases:

switch switchValue {
case .none:
    return "none"
case .one, .two:
    return "multiple values from case 2"
case .three, .four, .five:
    return "Multiple values from case 3"
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
Joe
  • 3,774
  • 1
  • 17
  • 25
51

how about :

struct Object{
    var type = 1
}

let lowType  = min(object1.type,object2.type)
let highType = max(object1.type,object2.type)
switch( lowType, highType )
{
  case (1,1):
      doSome()
  case (1,2):
      doSome2()
  case (1,3):
    doSome3()
  ...
  // you will only need to specify the combinations where the first
  // type is less or equal to the other.
}

If you will be doing this often, you could define a minMax function to further reduce the amount of code needed each time:

func minMax<T:Comparable>(_ a:T, _ b:T) ->(T,T) 
{ return a<b ? (a,b) : (b,a) }

switch minMax(object1.type,object2.type)
{
  case (1,1):
      doSome()
  case (1,2):
      doSome2()
  ...

note that you can also put each inverted pair in its case statement:

switch(object1.type,object2.type)
{
  case (1,1):
      doSome()
  case (1,2),(2,1):
      doSome2()
  case (1,3),(3,1):
      doSome3()
  ...
}

[UPDATE]

If you have more than two values to compare in a "permutation insensitive" fashion, you could expand on the minMax() idea by creating variants with more values:

func minToMax<T:Comparable>(_ a:T, _ b:T) -> (T,T) 
{ return a<b ? (a,b) : (b,a) }

func minToMax<T:Comparable>(_ a:T, _ b:T, _ c:T) -> (T,T,T) 
{ return { ($0[0],$0[1],$0[2]) }([a,b,c].sorted()) }

func minToMax<T:Comparable>(_ a:T, _ b:T, _ c:T, _ d:T) -> (T,T,T,T) 
{ return { ($0[0],$0[1],$0[2],$0[3]) }([a,b,c,d].sorted()) }

switch minToMax(object1.type,object2.type,object3.type)
{
   case (1,2,3)      : doSome1() // permutations of 1,2,3
   case (1,2,4)      : doSome2() // permutations of 1,2,4
   case (1,2,_)      : doSome3() // permutations of 1,2,anything else
   case (1,5...6,10) : doSome4() // more elaborate permutations
   ...
}

}

Alain T.
  • 40,517
  • 4
  • 31
  • 51
4

You can throw the objects into a set (which is inherently unordered) and check that the set contains a combination of elements. You have 15 (5! = 5+4+3+2+1) cases so you will have 15 ifs.

    enum PrimaryColor {
        case red, yellow, blue
    }
    enum SecondaryColor {
        case orange, green, purple
        init?(firstColor: PrimaryColor, secondColor: PrimaryColor) {
            let colors = Set<PrimaryColor>([firstColor, secondColor])
            if colors.contains(.red)  && colors.contains(.blue) {
                self = .purple
                return
            }
            if colors.contains(.yellow)  && colors.contains(.blue) {
                self = .green
                return
            }
            if colors.contains(.red)  && colors.contains(.yellow) {
                self = .orange
                return
            }
            //if you care about the diagonal check firstColor == secondColor  && colors.contains(.red) etc.
            return nil
        }
    }
Josh Homann
  • 15,933
  • 3
  • 30
  • 33
3

Assign object1.type and object2.type to two temporary variables (I called them a and b) such that a is always less than b. Now you only have to enumerate cases where the first number is less than the second.

struct Object {
    var type = 1
}

let (a, b) = object1.type < object2.type ? (object1.type, object2.type) : (object2.type, object1.type)
switch (a, b) {
case (1, 1): print("(1, 1)")
case (1, 2): print("(1, 2)")
case (1, 3): print("(1, 3)")
case (1, 4): print("(1, 4)")
case (1, 5): print("(1, 5)")
case (2, 2): print("(2, 2)")
case (2, 3): print("(2, 3)")
case (2, 4): print("(2, 4)")
case (2, 5): print("(2, 5)")
case (3, 3): print("(3, 3)")
case (3, 4): print("(3, 4)")
case (3, 5): print("(3, 5)")
case (4, 4): print("(4, 4)")
case (4, 5): print("(4, 5)")
case (5, 5): print("(5, 5)")

default: preconditionFailure("Values must each be in the range 1...5, with the first less than the second. Found: (\(a)\(b))")
}
Alexander
  • 59,041
  • 12
  • 98
  • 151