So I am creating a card game, Ring of Fire. I have stored images like this:
var picture:[UIImage] = [
UIImage(named: "Card2")!,
UIImage(named: "Card3")!,
UIImage(named: "Card4")!,
UIImage(named: "Card5")!,
UIImage(named: "Card6")!,
UIImage(named: "Card7")!,
UIImage(named: "Card8")!,
UIImage(named: "Card9")!,
UIImage(named: "Card10")!,
UIImage(named: "CardJack")!,
UIImage(named: "CardQueen")!,
UIImage(named: "CardKing")!,
UIImage(named: "CardAce")!,
]
Each card has text displayed under the current card:
var name:String = ""
var files = ["Velg en som må drikke", // 2
"Drikk selv", // 3
"Alle jenter må drikke", // 4
"Tommelen", // 5
"Alle gutter må drikke", // 6
"Pek på himmelen", // 7
"Drikkepartner", // 8
"Rim", // 9
"Kategori", // 10
"Lag en regel", // Jack
"Spørsmålsrunde", // Queen
"Hell drikke i koppen", // King
"Fossefall"] // Ace
And this is how I pick a random card:
func imageTapped(img: AnyObject){
if(cardsleftLabel.text != "0") {
let randomNumber = Int(arc4random_uniform(UInt32(files.count)))
let image = picture[randomNumber]
cardImage.image = image
name = files[randomNumber]
}
else{
print("No more cards")
}
}
The problem is that the card may appear many times, and that is wrong. There are 4 of each card, so how can I control that in my game? So the CardJack don't appear 6 times?
One way to do it is to generate an array of indices that represent your cards. Shuffle that array, and then remove the indices from that array as you draw a card.
// generate random list of indices from 0...12 four each
var cardIndices = (0...51).map {($0 % 13, arc4random())}.sort{$0.1 < $1.1}.map{$0.0}
// To get a card, remove last card from deck
let last = cardIndices.removeLast()
// use the index to look up the picture
let randomCard = picture[last]
// It's also easy to check how many cards you have left in your deck
let remaining = cardIndices.count
This works by first creating an array of tuples which contain a number from 0...12 and a some random integer. Then that array is sorted by the random integer element in the tuple, and then map is used to separate out just the array of indices leaving you with a random array of Int with values from 0...12 (four values of each).
Here it is in class form.
import UIKit
struct Card {
let image: UIImage
let text: String
}
class Deck {
private let cards:[Card] = [
Card(image: UIImage(named: "Card2")!, text: "Velg en som må drikke"),
Card(image: UIImage(named: "Card3")!, text: "Drikk selv"),
Card(image: UIImage(named: "Card4")!, text: "Alle jenter må drikke"),
Card(image: UIImage(named: "Card5")!, text: "Tommelen"),
Card(image: UIImage(named: "Card6")!, text: "Alle gutter må drikke"),
Card(image: UIImage(named: "Card7")!, text: "Pek på himmelen"),
Card(image: UIImage(named: "Card8")!, text: "Drikkepartner"),
Card(image: UIImage(named: "Card9")!, text: "Rim"),
Card(image: UIImage(named: "Card10")!, text: "Kategori"),
Card(image: UIImage(named: "CardJack")!, text: "Lag en regel"),
Card(image: UIImage(named: "CardQueen")!, text: "Spørsmålsrunde"),
Card(image: UIImage(named: "CardKing")!, text: "Hell drikke i koppen"),
Card(image: UIImage(named: "CardAce")!, text: "Fossefall")
]
private var cardIndices = [Int]()
var cardsInDeck: Int { return cardIndices.count }
func shuffleCards() {
cardIndices = (0...51).map{($0 % 13, arc4random())}.sort{$0.1 < $1.1}.map{$0.0}
}
func drawCard() -> Card {
if cardIndices.count == 0 {
shuffleCards()
}
let last = cardIndices.removeLast()
return cards[last]
}
}
Notes:
The cards and cardIndices have been made private to hide those details from a user of Deck.
Thanks to #Paulw11's suggestion, this solution now uses a struct to represent a card. This keeps the data together and provides a nice value that can be returned from drawCard.
The user of a Deck can create a Deck with Deck(), they can call shuffleCards() to randomize the deck, check the cardsInDeck property to find out how many shuffled cards are available, and they can call drawCard() to get the next card from the deck.
How to Use
For the viewController that controls the deck, add a property to the viewController:
class MyGame: UIViewController {
var deck = Deck()
// the rest of the code
}
Then when you need a card, for example inside of an #IBAction for a button, just call deck.drawCard:
#IBAction func turnOverNextCard(button: UIButton) {
let card = deck.drawCard()
// Use the image and text to update the UI
topCardImageView.image = card.image
topCardLabel.text = card.text
// I'm not going to wait for the deck to shuffle itself
if deck.cardsInDeck < 10 {
deck.shuffleCards()
}
}
Splitting Hairs: A Better Shuffle
My shuffle routine shuffles the deck by associating a random UInt32 with each card and then sorting the deck by those values. If the same random number is generated for two cards, then it is possible that earlier cards in the deck would be favored over later cards (or vice versa depending on the sort algorithm). This is really splitting hairs, but in the interest of providing the best shuffle possible, I provide the following alternative:
func shuffleCards() {
cardIndices = (0...51).map {$0 % 13}
for i in (1...51).reverse() {
let rand = Int(arc4random_uniform(UInt32(i + 1)))
(cardIndices[i], cardIndices[rand]) = (cardIndices[rand], cardIndices[i])
}
}
This algorithm is based upon the Fisher-Yates shuffle.
You need a class to represent the Deck of cards, like this.
class Deck {
static let seeds = 4
var images : [UIImage:Int] = [
UIImage(named: "Card2")! : seeds,
UIImage(named: "Card3")! : seeds,
UIImage(named: "Card4")! : seeds,
UIImage(named: "Card5")! : seeds,
UIImage(named: "Card6")! : seeds,
UIImage(named: "Card7")! : seeds,
UIImage(named: "Card8")! : seeds,
UIImage(named: "Card9")! : seeds,
UIImage(named: "Card10")! : seeds,
UIImage(named: "CardJack")! : seeds,
UIImage(named: "CardQueen")! : seeds,
UIImage(named: "CardKing")! : seeds,
UIImage(named: "CardAce")! : seeds
]
func extractRandomCard() -> UIImage? {
let flatten = images.reduce([UIImage]()) { [UIImage](count: $0.1.1, repeatedValue: $0.1.0) }
guard !flatten.isEmpty else { return nil }
let random = Int(arc4random_uniform(UInt32(flatten.count)))
let selectedCard = flatten[random]
images[selectedCard] = images[selectedCard]! - 1
return selectedCard
}
}
Now you can extract cards from the deck writing
let deck = Deck()
let image = deck.extractRandomCard()
Each time you extract a card the Deck keeps track of it and it won't let you extract more than 4 times the same card.
I didn't test it... but it should work
An alternative approach to the random-stuff is to use some of Apple's nice GamePlayKit-stuff created exactly for these kind of use-cases. If you have the cards array you could for instance just do:
let shuffled2 = GKRandomSource().arrayByShufflingObjectsInArray(cards) as! [Card]
or you could keep the array unshuffled and instead shuffle the indexes you request:
let indexes = GKShuffledDistribution.init(forDieWithSideCount: 52)
// going through all the cards randomly
for _ in cards {
let card = cards[indexes.nextInt()]
}
public enum Palo: Int
{
case Corazones = 1
case Treboles = 2
case Picas = 3
case Diamantes = 4
}
public struct Card
{
public var text: String
public var position: Int
public var palo: Palo
public func description() -> String
{
return "\(self.position) of \(self.palo) -- \(self.text)"
}
}
public struct Deck
{
public var cards: [Card]
public init()
{
cards = [Card]()
for number in 1...13
{
for palo in 1...4
{
let card: Card = Card(text: "", position: number, palo: Palo(rawValue: palo)!)
cards.append(card)
}
}
}
/**
Return cards one by one
*/
public mutating func randomCard() -> Card?
{
guard !self.cards.isEmpty else
{
return nil
}
let position: Int = Int(arc4random_uniform(UInt32(self.cards.count)))
let card: Card = self.cards.removeAtIndex(position)
return card
}
}
//
// TEST
//
var deck: Deck = Deck()
for index in 1...200
{
if let card = deck.randomCard()
{
print("\(index) -- \(card.description())")
}
}
Related
I have an app with 2 image views. each one randomly displays an image of a dice face (from 1 to 6), the end results looks like the simulation of a dice toss for a player and for the computer.
it looks like this :
I have an array that contains the names for each image that is displayed :
let des: Array = ["one", "two", "three", "four", "five", "six"]
I also have a function that randomizes the image that is displayed for the 2 players
UIView.animate(withDuration: 0.5) {
self.dicePlayer.image = UIImage(named: self.des.randomElement() ?? "one")
self.diceComputer.image = UIImage(named: self.des.randomElement() ?? "two")
}
// on appelle la fonction qui gere les scores
gestionDesScores()
}
I would like to make comparison between those 2 tosses (i.e : if player one dice toss is superior to player 2 toss, then the player wins and vice versa).
Have your dice values in an enum:
enum Die: String,CaseIterable{
case One = "one"
case Two = "two"
case Three = "three"
case Four = "four"
case Five = "five"
case Six = "six"
}
Add an extension :
extension CaseIterable where Self: Equatable {
var index: Self.AllCases.Index? {
return Self.allCases.firstIndex { self == $0 }
}
}
And your throwDie method can look like :
func throwDice(){
let userResult = Die.allCases.randomElement()
let computerResult = Die.allCases.randomElement()
UIView.animate(withDuration: 0.2, animations: {
self.dicePlayer.image = UIImage(named: userResult?.rawValue ?? "one")
self.diceComputer.image = UIImage(named: computerResult?.rawValue ?? "one")
}) { (_) in
print("Index Value : \(computerResult?.index) : \(computerResult?.rawValue)")
//You may use the indexValue for comparison and the rawValue to assign your image
}
}
Self explanatory, I believe.
You can try
let fImg = self.des.randomElement() ?? "one"
let sImg = self.des.randomElement() ?? "two"
UIView.animate(withDuration: 0.5) {
self.dicePlayer.image = UIImage(named:fImg)
self.diceComputer.image = UIImage(named:sImg)
}
Then
if let fIndex = des.index(of:fImg) ,
let sIndex = des.index(of:sImg) ,
fIndex == (sIndex - 1) {
// wins
}
I would recommend storing the two tosses as human readable Int values. Add these properties to your class:
var playerToss = 1
var computerToss = 1
Then modify your function like this:
func lancerDeDes() {
self.playerToss = Int.random(in: 1...6)
self.computerToss = Int.random(in: 1...6)
UIView.animate(withDuration: 0.5) {
// subtract 1 because des array is indexed 0...5 not 1...6
self.dicePlayer.image = UIImage(named: self.des[self.playerToss - 1])
self.diceComputer.image = UIImage(named: self.des[self.computerToss - 1])
}
// on appelle la fonction qui gere les scores
gestionDesScores()
}
Then you can just compare playerToss and computerToss directly:
if self.playerToss > self.computerToss {
// the player wins!
}
Storing the values as Int will aid you in debugging in addition to making the comparison easier.
Magic Numbers
In your program, I think it is clear what 6 represents. In general though it is wise to avoid magic numbers (uncommented numbers) in your code. So you could write the rolls as:
self.playerToss = Int.random(in: des.indices) + 1
self.computerToss = Int.random(in: des.indices) + 1
By using des.indices to obtain the range 0...5 from the des array, you ensure that the code would work correctly even if the number of values in the des array changed.
I personally would still store the rolls in the range 1...6 for debugging purposes, but you could avoid the + 1 and - 1 by storing the rolls in the range 0...5.
I'm working on an app which lets users have randomized data when you shake your device.
I have 4 arrays to hold the string data and function which creates randomized number;
let characters = ["Zoolog", "Xander"]
let problems = ["Asteroid", "Dr Evil"]
let places = ["Vast Desert", "Ice Caves"]
let time = ["Wednesday 12th, 1220", "1236"]
func randomCharacter() -> String {
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: characters.count)
return characters[randomNumber]
}
func randomPlaces() -> String {
let randomNumberOne = GKRandomSource.sharedRandom().nextInt(upperBound: places.count)
return places[randomNumberOne]
}
func randomProblems() -> String {
let randomNumberTwo = GKRandomSource.sharedRandom().nextInt(upperBound: problems.count)
return problems[randomNumberTwo]
}
func randomTime() -> String {
let randomNumberThree = GKRandomSource.sharedRandom().nextInt(upperBound: time.count)
return time[randomNumberThree]
}
On my viewController, data is randomized and users get a randomized data on their screen once they shake their devices.
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if(event?.subtype == UIEventSubtype.motionShake) {
characterName.text = myStoryCharacters.randomCharacter()
placeName.text = myStoryCharacters.randomPlaces()
problemName.text = myStoryCharacters.randomProblems()
timeName.text = myStoryCharacters.randomTime()
}
}
I also have an imageView for the character picture. So once the data is randomized I would like my users to see the characters and their names as well. But at the moment, I can only randomize the imageView and characters separately not together.
I've gone through some sample codes but couldn't understand how to approach this.
--Updated
Note: I don't have a problem with the code I have. I don't know how to randomize the characterImageView to match with the characterName. So if a picture belongs to a character in my character array then the nameLabel and imageView should match.
override func viewDidLoad() {
super.viewDidLoad()
super.becomeFirstResponder()
// Do any additional setup after loading the view, typically from a nib.
characterImageView.image = UIImage(named: "elegantEmma")
placeImageView.image = UIImage(named: "zombieLand")
problemImageView.image = UIImage(named: "meteor")
timeImageView.image = UIImage(named: "time")
//First random value shown on the launch
characterName.text = myStoryCharacters.randomCharacter()
placeName.text = myStoryCharacters.randomPlaces()
problemName.text = myStoryCharacters.randomProblems()
timeName.text = myStoryCharacters.randomTime()
You need to store the image names in arrays as well, so for example for your characters have...
let characters = ["Zoolog", "Xander"]
let characterImages = ["ZoologImage", "XanderImage"] // These relate to the image names in your assets
func randomCharacter() -> String {
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: characters.count)
characterImageView.image = UIImage(named: characterImages[randomNumber])
return characters[randomNumber]
}
Then do the same for the other arrays, you could also change the random functions to set the text and not bother returning it, unless of course you need it for something else
I have an array in my VC
var list : [QCategoryy] = [QCategoryy]()
list = NearbyPlaces.getCategories()
where getCategories() is
static func getCategories() -> [QCategoryy] {
let list:[QCategoryy] = [QCategoryy(name: "bar", image: UIImage(named: "bar_button.png")!), QCategoryy(name :"night_club", image: UIImage(named: "nightclub_button.png")!), QCategoryy(name: "movie_theater", image: UIImage(named: "cinema_button.png")!), QCategoryy(name: "restaurant", image: UIImage(named: "restaurant_button.png")!), QCategoryy(name: "gym", image: UIImage(named: "gym_button.png")!), QCategoryy(name: "spa", image: UIImage(named: "spa_button.png")!), QCategoryy(name: "museum", image: UIImage(named: "museum_button.png")!)]
return list
}
but i would like that in my viewController the user can choose the maximum number of objects that must be in this array with a stepper Int(steppeR.value) (for example if the value of the stepper is 1, in my list can be only the first item of getCategories) I also already have an extension for shuffle the array
extension MutableCollection where Indices.Iterator.Element == Index {
/// Shuffles the contents of this collection.
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
guard d != 0 else { continue }
let i = index(firstUnshuffled, offsetBy: d)
self.swapAt(firstUnshuffled, i)
}
}
}
extension Sequence {
/// Returns an array with the contents of this sequence, shuffled.
func shuffled() -> [Iterator.Element] {
var result = Array(self)
result.shuffle()
return result
}
}
because i do not want that with the limitation there are always the same items. How can i do something like this?
With your static func getCategories() you initialize your main list right ?
Then, when the user changes the value of the stepper Int(steppeR.value), you create a filtered list with only the number of objects needed :
//stepper is equal to 1
var filteredList = [QCategoryy]()
filteredList.append(list[0])
//stepper is equal to 3
var filteredList = [QCategoryy]()
for i in 0..<3 {
filteredList.append(list[i])
}
I have this code and it's working, but not as really want to. I'm new to Xcode/Swift/developing apps. I've made it watching tutorials and using some snippets as examples, but so far is working :)
I'd like to make it work in this way:
When I click on playSound and hear it, then I have to choose between leftImage and rightImage..the image that matches with the playSound. I've tried into many ways to get it work.. but nothing...I cannot make the sound index a string to compare as "if a == b .."
When I open the app, it shows me only two bottom buttons.. but not any image. How can I make it to show the first image?
I also like to make it a bit random think... When I click on "nextImage" button, I'd like to shows to different images but only one linked to the sound..so when I play the sound..had to check only the photo who matches with the sound.
At this moment, I have only 8 photos/sounds into the array, but when I click more than 9 times on nextImage.. the images are going on and on.. but the sound starts from beginning and it's not linked any more. for example, at the 10th image..it playSound says it's at 1. How can I make It to follow the image index?
How to convert the image index into text? for example, if its shows me the image "foto1"; I'd like to show me under the image a text in the label.
import UIKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var soundFiles: [String] = [
"s0",
"s1",
"s2",
"s3",
"s4",
"s5",
"s6",
"s7",
"s8",
"s9"
]
var images1: [UIImage] = [
UIImage(named: "foto1.png")!,
UIImage(named: "foto2.png")!,
UIImage(named: "foto3.png")!,
UIImage(named: "foto4.png")!,
UIImage(named: "foto5.png")!,
UIImage(named: "foto6.png")!,
UIImage(named: "foto7.png")!,
UIImage(named: "foto8.png")!
]
var images2: [UIImage] = [
UIImage(named: "foto1.png")!,
UIImage(named: "foto2.png")!,
UIImage(named: "foto3.png")!,
UIImage(named: "foto4.png")!,
UIImage(named: "foto5.png")!,
UIImage(named: "foto6.png")!,
UIImage(named: "foto7.png")!,
UIImage(named: "foto8.png")!
]
var happySad: [UIImage] = [
UIImage(named: "sad.png")!,
UIImage(named: "happy.png")!
]
var currentImageIndex = 0
var currentImage2Index = 0
#IBOutlet weak var leftImage: UIImageView!
#IBOutlet weak var rightImage: UIImageView!
#IBOutlet weak var sh: UIImageView!
#IBAction func nextImages(_ sender: Any) {
currentImageIndex += 1
let numberOfImages = images1.count
let nextImage1Index = currentImageIndex % numberOfImages
leftImage.image = images1[nextImage1Index]
leftImage.isUserInteractionEnabled = true
self.view.addSubview(leftImage)
let gesture1 = UITapGestureRecognizer(target: self, action: #selector(ViewController.singleTap1))
leftImage.addGestureRecognizer(gesture1)
currentImage2Index += 1
let numberOfImages2 = images2.count
let nextImage2Index = currentImage2Index % numberOfImages2
rightImage.image = images2[nextImage2Index]
sh.image = UIImage(named: "question")
rightImage.isUserInteractionEnabled = true
self.view.addSubview(rightImage)
let gesture2 = UITapGestureRecognizer(target: self, action: #selector(ViewController.singleTap2))
rightImage.addGestureRecognizer(gesture2)
}
func singleTap1() {
if currentImageIndex == currentImage2Index {
sh.image = UIImage(named: "happy.png")
print("ok")
} else {
sh.image = UIImage(named: "sad.png")
print("not ok")
}
}
func singleTap2() {
if currentImageIndex == currentImage2Index {
sh.image = UIImage(named: "happy.png")
print("ok2")
} else {
sh.image = UIImage(named: "sad.png")
print("not ok2")
}
}
var player: AVAudioPlayer!
#IBAction func playSound(_ sender: Any) {
let numberOfImages = images1.count
let nextImage5Index = currentImageIndex % numberOfImages
let soundFilePath = Bundle.main.url(forResource: soundFiles[nextImage5Index], withExtension: ".m4a")!
player = try! AVAudioPlayer(contentsOf: soundFilePath)
player.prepareToPlay()
player.play()
}
}
I think you are over complicating the issue.
I would personally create a struct that contains each 'level' of the game.
enum correctImageType {
case left, right
}
struct Level {
var word: String
var leftImage: UIImage
var rightImage: UIImage
var soundFile: String
var correctImage: correctImageType
}
var level1 = Level(word: "Dog", leftImage: UIImage(named: "dog"), rightImage: UIImage(named: "cat", soundFile: "Woof", correctImage: .left)
This gives you enough information in one data structure to display each level. You can then create an array of these items, sort them, random sort them, mark as complete as each is done etc.
There are alot of questions within your question. I would try to make it more specific and tackle one particular part of the problem at a time. There is no problem in posting multiple questions as long as they are different.
This question already has answers here:
How do I shuffle an array in Swift?
(25 answers)
Closed 6 years ago.
Noob here, I am trying to create a card game using a standard deck of 52 cards. Here is the code I would like to make into a func so I dont have to manually write it out every single time. I am creating 5 different players so this would be replicated to other players.
func firstPlayerCardShuffle() {
var firstPlayerCard1Num = (Int(arc4random_uniform(UInt32(13)) + 2))
var firstPlayerCard1Suit = suits[Int(arc4random_uniform(UInt32(suits.count)))]
var firstPlayerCard1 = "\(firstPlayerCard1Num) of \(firstPlayerCard1Suit)"
var firstPlayerCard2Num = (Int(arc4random_uniform(UInt32(13)) + 2))
var firstPlayerCard2Suit = suits[Int(arc4random_uniform(UInt32(suits.count)))]
var firstPlayerCard2 = "\(firstPlayerCard2Num) of \(firstPlayerCard2Suit)"
return(firstPlayerCard1,firstPlayerCard2)
}
Can someone let me know what I'm missing.
Not a direct answer to your question, but I think you'll want something like this:
enum Number: String {
case Two = "two"
case Three = "three"
case Four = "four"
case Five = "five"
case Six = "six"
case Seven = "seven"
case Eight = "eight"
case Nine = "nine"
case Ten = "ten"
case Jack = "jack"
case Queen = "queen"
case King = "king"
case Ace = "ace"
static var randomNumber: Number {
return [Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace][Int(arc4random_uniform(13))]
}
}
enum Suit: String {
case Spades = "spades"
case Hearts = "hearts"
case Diamonds = "diamonds"
case Clubs = "clubs"
static var randomSuit: Suit {
return [Spades, Hearts, Diamonds, Clubs][Int(arc4random_uniform(4))]
}
}
struct Card: CustomStringConvertible, Equatable {
let number: Number
let suit: Suit
var description: String {
return "\(number.rawValue) of \(suit.rawValue)"
}
static var randomCard: Card {
return Card(number: Number.randomNumber, suit: Suit.randomSuit)
}
static func randomCards(count count: Int) -> [Card] {
guard count > 0 else {
return []
}
guard count <= 52 else {
fatalError("There only are 52 unique cards.")
}
let cards = randomCards(count: count - 1)
while true {
let card = randomCard
if !cards.contains(card) {
return cards + [card]
}
}
}
}
func == (left: Card, right: Card) -> Bool {
return left.number == right.number && left.suit == right.suit
}
let randomCards = Card.randomCards(count: 5)
print(randomCards)
// prints five random cards
Let me know if you have any other questions.