Swift check for tie game logic

Discussion in 'iOS Programming' started by patent10021, Feb 28, 2015.

  1. patent10021, Feb 28, 2015
    Last edited: Mar 15, 2015

    patent10021 macrumors 68020

    patent10021

    Joined:
    Apr 23, 2004
    #1
    I have finished my game of Tic Tac Toe with MPC class and it all works great except for the handlePotentialTie function. Actually there are zero errors and crashes but the logic behaves oddly when the board is full. Without the handlePotentialTie function the game works perfectly. I added this function later on and it's towards the bottom. I have included my code and a screenshot of the behaviour. I'm totally new to programming and any help would be insanely appreciated. Thanks in advance.


    Code:
    //
    //  ViewController.swift
    //  Tic Tac Toe
    
    import UIKit
    import MultipeerConnectivity
    
    class ViewController: UIViewController, MCBrowserViewControllerDelegate {
    
        @IBOutlet var fields: [TTTImageView]!
        var currentPlayer: String!
    
        var appDelegate:AppDelegate!
        
        var turn:String!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
            appDelegate.mpcHandler.setupPeerWithDisplayName(UIDevice.currentDevice().name)
            appDelegate.mpcHandler.setupSession()
            appDelegate.mpcHandler.advertiseSelf(true)
            
            NSNotificationCenter.defaultCenter().addObserver(self, selector: "peerChangedStateWithNotification:", name: "MPC_DidChangeStateNotification", object: nil)
        
            NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleReceivedDataWithNotification:", name: "MPC_DidReceiveDataNotification", object: nil)
            
            setupField()
            currentPlayer = "letterX"
            turn = "yourTurn"
        }
    
        
        @IBAction func connectWithPlayer(sender: AnyObject) {
            if appDelegate.mpcHandler.session != nil{
                appDelegate.mpcHandler.setupBrowser()
                appDelegate.mpcHandler.browser.delegate = self
                
                self.presentViewController(appDelegate.mpcHandler.browser, animated: true, completion: nil)
            }
        }
        
        
        
        func peerChangedStateWithNotification(notification:NSNotification){
            let userInfo = NSDictionary(dictionary: notification.userInfo!)
            
            let state = userInfo.objectForKey("state") as Int
            
            if state != MCSessionState.Connecting.rawValue{
                self.navigationItem.title = "Connected"
            }
            
        }
        
        
        func handleReceivedDataWithNotification(notification:NSNotification){
            let userInfo = notification.userInfo! as Dictionary
            let receivedData:NSData = userInfo["data"] as NSData
        
            let message = NSJSONSerialization.JSONObjectWithData(receivedData, options: NSJSONReadingOptions.AllowFragments, error: nil) as NSDictionary
            let senderPeerId:MCPeerID = userInfo["peerID"] as MCPeerID
            let senderDisplayName = senderPeerId.displayName
            println(message)
           
            if message.objectForKey("string")?.isEqualToString("New Game") == true{
                turn = "yourTurn"
                let alert = UIAlertController(title: "Tic Tac Toe", message: "\(senderDisplayName) has started a new game", preferredStyle: UIAlertControllerStyle.Alert)
                
                alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (alert:UIAlertAction!) -> Void in
                    self.resetField()
                }))
                
                self.presentViewController(alert, animated: true, completion: nil)
                
                
            }else if message.objectForKey("turnString")?.isEqualToString("deviceA") == true{
                
                turn = "deviceA"
                
                var field:Int? = message.objectForKey("field")?.integerValue
                var player:String? = message.objectForKey("player")as? String
                
                if field != nil && player != nil{
                    fields[field!].player = player
                    fields[field!].setPlayer(player!)
                    
                    if player == "letterX"{
                        currentPlayer = "letterO"
                    }else{
                        currentPlayer = "letterX"
                    }
                    
                    checkResults()
                    
                }
            
            }
     
        
        }
        
        
        func fieldTapped(recognizer:UITapGestureRecognizer){
            let tappedField = recognizer.view as TTTImageView //
            
            
            if turn == "deviceA" || turn == "yourTurn"{
                tappedField.setPlayer(currentPlayer)
                let messageDict = ["field":tappedField.tag, "player":currentPlayer, "turnString":"deviceA"]
                
                let messageData = NSJSONSerialization.dataWithJSONObject(messageDict, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
                
                let data = messageData
                // do something with the returned data
                var error: NSError?
                appDelegate.mpcHandler.session.sendData(data, toPeers: appDelegate.mpcHandler.session.connectedPeers, withMode: MCSessionSendDataMode.Unreliable, error:&error)
                
                if error != nil{
                    println("error:\(error?.localizedDescription)")
                }
            }
            
            checkResults()
            turn = "deviceB"
        }
        
        func setupField(){
            for index in 0...fields.count - 1{
                let gestureRecognizer = UITapGestureRecognizer(target: self, action: "fieldTapped:")
                gestureRecognizer.numberOfTapsRequired = 1
                
                fields[index].addGestureRecognizer(gestureRecognizer)
                
            }
        }
        
        func resetField(){
            for index in 0...fields.count - 1 {
                fields[index].image = nil
                fields[index].activated = false
                fields[index].player = ""
            }
            
            currentPlayer = "letterX"
        }
        
        @IBAction func newGame(sender: AnyObject) {
            resetField()
            
            let messageDict = ["string":"New Game"]
            
            let messageData = NSJSONSerialization.dataWithJSONObject(messageDict, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
            
            var error:NSError?
            
            appDelegate.mpcHandler.session.sendData(messageData, toPeers: appDelegate.mpcHandler.session.connectedPeers, withMode: MCSessionSendDataMode.Reliable, error: &error)
            
            if error != nil{
                println("error: \(error?.localizedDescription)")
            }
            
            
        }
        
    
        
        
        func checkResults(){
            var winner = ""
            
            if fields[0].player == "letterX" && fields[1].player == "letterX" && fields[2].player == "letterX"{
                winner = "letterX"
            }else if fields[0].player == "letterO" && fields[1].player == "letterO" && fields[2].player == "letterO"{
                winner = "letterO"
            }else if fields[3].player == "letterX" && fields[4].player == "letterX" && fields[5].player == "letterX"{
                winner = "letterX"
            }else if fields[3].player == "letterO" && fields[4].player == "letterO" && fields[5].player == "letterO"{
                winner = "letterO"
            }else if fields[6].player == "letterX" && fields[7].player == "letterX" && fields[8].player == "letterX"{
                winner = "letterX"
            }else if fields[6].player == "letterO" && fields[7].player == "letterO" && fields[8].player == "letterO"{
                winner = "letterO"
            }else if fields[0].player == "letterX" && fields[3].player == "letterX" && fields[6].player == "letterX"{
                winner = "letterX"
            }else if fields[0].player == "letterO" && fields[3].player == "letterO" && fields[6].player == "letterO"{
                winner = "letterO"
            }else if fields[1].player == "letterX" && fields[4].player == "letterX" && fields[7].player == "letterX"{
                winner = "letterX"
            }else if fields[1].player == "letterO" && fields[4].player == "letterO" && fields[7].player == "letterO"{
                winner = "letterO"
            }else if fields[2].player == "letterX" && fields[5].player == "letterX" && fields[8].player == "letterX"{
                winner = "letterX"
            }else if fields[2].player == "letterO" && fields[5].player == "letterO" && fields[8].player == "letterO"{
                winner = "letterO"
            }else if fields[0].player == "letterX" && fields[4].player == "letterX" && fields[8].player == "letterX"{
                winner = "letterX"
            }else if fields[0].player == "letterO" && fields[4].player == "letterO" && fields[8].player == "letterO"{
                winner = "letterO"
            }else if fields[2].player == "letterX" && fields[4].player == "letterX" && fields[6].player == "letterX"{
                winner = "letterX"
            }else if fields[2].player == "letterO" && fields[4].player == "letterO" && fields[6].player == "letterO"{
                winner = "letterO"
            }
    
            
    
            
            if winner != ""{
                let alert = UIAlertController(title: "Tic Tac Toe", message: "The winner is \(winner)", preferredStyle: UIAlertControllerStyle.Alert)
                alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (alert:UIAlertAction!) -> Void in
                    self.resetField()
                }))
                
                self.presentViewController(alert, animated: true, completion: nil)
            
           }
           
    // This is the area I don't know how to code.
    // Thanks for looking
    //
    //
    
    
            func handlePotentialTie() -> Bool {
                if contains(fields, { $0.player == "" }) {
                    // An empty field: the game isn't over.
                    return false
                    
                } else {
                    contains(fields, { $0.player != "" })
                    // No empty fields: the game is over and must be a tie.
                    // End game and announce the tie.
                    
                    let alert = UIAlertController(title: "Tic Tac Toe", message: "Tie Game", preferredStyle: UIAlertControllerStyle.Alert)
                    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (alert:UIAlertAction!) -> Void in
                        self.resetField()
                    }))
                    
                    self.presentViewController(alert, animated: true, completion: nil)
                    
                    
                    return true
                }
                
            }
            
            
    }
        
       
        
        
        func browserViewControllerDidFinish(browserViewController: MCBrowserViewController!) {
            appDelegate.mpcHandler.browser.dismissViewControllerAnimated(true, completion: nil)
        }
        
        func browserViewControllerWasCancelled(browserViewController: MCBrowserViewController!) {
            appDelegate.mpcHandler.browser.dismissViewControllerAnimated(true, completion: nil)
        }
        
        
        
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
    }
    
     
  2. chown33, Feb 28, 2015
    Last edited: Feb 28, 2015

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    Make a simplified test case. Run it in the timeline (or whatever tools Swift has for debugging).

    The test case should first fill the playing board (i.e. the fields array) with a known tie game. It should then call your checkResults() function. Then you should trace through the call of checkResults and see exactly which branch is being executed.

    Creating and running test cases is one of the fundamental tools of debugging. Test cases embody the debugging principle "Confirm Your Expectations". A test case is a programmed statement of what is expected.


    If this is your first non-trivial Swift program, then you need to learn how to debug non-trivial Swift programs.

    If you've never used Swift's debugging tools before, learn to use them now, because you'll need to know them very well if you intend to write anything else.


    You could also benefit by learning about factoring (Break Things Down), but that can be left for another thread. As a brief exercise, what commonalities do you see in your if/else chain? Think about how you'd write code to express the commonality of those things, with a few smaller functions and less overt repetition.

    Also, your huge if/else chain doesn't look for a diagonal win in positions {2,4,6}, possibly due to the sheer size and complexity of you reading the if/else chain.
     
  3. patent10021 thread starter macrumors 68020

    patent10021

    Joined:
    Apr 23, 2004
    #3
    Hi

    Thanks for your input. Since I'm using an array fields 3,5,7 are actually 2,4,6.

    It's difficult to debug because I'm not getting errors or crashes. Since I'm new I'll have to experiment with placement as you said.

    I put the tie function inside of the check for winner function so I guess that's why it's all funny.

    I'll try it in a couple other places and see what happens. Swift has a "playground" so I'll test it in that.

    Thanks again.

    EDIT: I see you corrected your arrays but those arrays are there at the bottom.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    By golly, so they are. I didn't see them for all the previous barrage of if/else and such.
     
  5. Boris-VTR, Mar 1, 2015
    Last edited: Mar 1, 2015

    Boris-VTR macrumors regular

    Joined:
    Apr 18, 2013
    #5
    Not sure, but having two "let alert" declaration is not good. They are constants and should not be declared multiple times (i would expect Xcode to give you error for this).
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    It's not an error. They're in two separate contexts/scopes.
     
  7. Boris-VTR macrumors regular

    Joined:
    Apr 18, 2013
    #7
    Yes, they are not in the same scope. I quickly went through code and wasn't paying attention.
     

Share This Page