Reputation: 61
I am trying to initialize a deck of cards. I have card attributes in my card struct. My approach is to try and create an array of "enum states", then iterate through those to initialize each card. I am having trouble doing so.
Game Class
import Foundation
struct Set{
var cards = [Card]()
init(){
let properties : [Any] =
[cardShape.self, cardColor.self, cardNumber.self, cardShading.self]
for prop in properties{
// Not really sure how to iterate through this array...
// Ideally it would be something like this.
// Iterate through array, for property in array,
// card.add(property)
}
}
}
Card Class
import UIKit
import Foundation
struct Card{
var attributes : properties = properties()
mutating func addProperty(value : Property){
if value is cardShape{
attributes.shape = value as! cardShape
} else if value is cardColor{
attributes.color = value as! cardColor
} else if value is cardNumber{
attributes.number = value as! cardNumber
}else if value is cardShading{
attributes.shading = value as! cardShading
}else{
print("error")
}
}
}
protocol Property{
static var allValues : [Property] {get}
}
struct properties{
var shape : cardShape = cardShape.none
var color : cardColor = cardColor.none
var number : cardNumber = cardNumber.none
var shading : cardShading = cardShading.none
}
enum cardShape : String,Property{
case Square = "■"
case Triangle = "▲"
case Circle = "●"
case none
static var allValues : [Property]{ return [cardShape.Square,cardShape.Triangle,cardShape.Circle]}
}
enum cardColor:Property {
case Red
case Purple
case Green
case none
static var allValues : [Property] {return [cardColor.Red,cardColor.Purple,cardColor.Green]}
}
enum cardNumber : Int,Property{
case One = 1
case Two = 2
case Three = 3
case none
static var allValues : [Property] {return [cardNumber.One,cardNumber.Two,cardNumber.Three]}
}
enum cardShading: Property {
case Solid
case Striped
case Outlined
case none
static var allValues : [Property] {return [cardShading.Solid,cardShading.Striped,cardShading.Outlined]}
}
So to summarize, my main issue is trying to create an array of enums, then cycling through the enum states to initialize a card with specific attribute states.
Upvotes: 0
Views: 438
Reputation: 154563
You will want to make sure you cover all combinations of attributes and make sure each card has one of each of the four types of attributes. I would suggest using nested loops:
for shape in cardShape.allValues {
for color in cardColor.allValues {
for number in cardNumber.allValues {
for shading in cardShading.allValues {
var card = Card()
card.addProperty(shape)
card.addProperty(color)
card.addProperty(number)
card.addProperty(shading)
cards.append(card)
}
}
}
}
I believe your Card
struct
is a bit too complex. If you change your representation, it will be easier to create the cards.
Have your card represent the different attributes as their own property:
struct Card {
let shape: CardShape
let color: CardColor
let number: CardNumber
let shading: CardShading
}
Then use nested loops to create your cards:
for shape in CardShape.allValues {
for color in CardColor.allValues {
for number in CardNumber.allValues {
for shading in CardShading.allValues {
cards.append(Card(shape: shape, color: color, number: number, shading: shading))
}
}
}
}
Notes:
allValues
properties to return arrays of the specific attribute type (for example [CardShape]
).Alternate Answer:
Instead of using nested arrays, you could use MartinR's combinations
function to create the list of combinations of the properties. Adding an init
to Card
that takes [Property]
, you can create the cards in two lines of code:
struct Card {
var shape = CardShape.none
var color = CardColor.none
var number = CardNumber.none
var shading = CardShading.none
init(properties: [Property]) {
for property in properties {
switch property {
case let shape as CardShape:
self.shape = shape
case let color as CardColor:
self.color = color
case let number as CardNumber:
self.number = number
case let shading as CardShading:
self.shading = shading
default:
break
}
}
}
}
// https://stackoverflow.com/a/45136672/1630618
func combinations<T>(options: [[T]]) -> AnySequence<[T]> {
guard let lastOption = options.last else {
return AnySequence(CollectionOfOne([]))
}
let headCombinations = combinations(options: Array(options.dropLast()))
return AnySequence(headCombinations.lazy.flatMap { head in
lastOption.lazy.map { head + [$0] }
})
}
struct SetGame {
let cards: [Card]
init(){
let properties: [Property.Type] = [CardShape.self, CardColor.self, CardNumber.self, CardShading.self]
cards = combinations(options: properties.map { $0.allValues }).map(Card.init)
}
}
How this works:
properties.map { $0.allValues }
calls allValues
on each item of the properties
array creating an [[Property]]
with [[.square, .triangle, .circle], [.red, .purple, .green], [.one, .two, .three], [.solid, .striped, .outlined]]
combinations
which creates a sequence with all 81 combinations of these properties: [[.square, .red, .one, .solid], ..., [.circle, .green, .three, .outlined]]
.map
is run on this sequence to call Card.init
with each combination which results in an [Card]
with 81 cards.Upvotes: 1