Swift check for tie game logic

patent10021

macrumors 68030
Original poster
Apr 23, 2004
2,907
394
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.
    }


}
 
Last edited:

chown33

Moderator
Staff member
Aug 9, 2009
8,356
4,330
Gourd City
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.
 
Last edited:

patent10021

macrumors 68030
Original poster
Apr 23, 2004
2,907
394
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.
 

Boris-VTR

macrumors regular
Apr 18, 2013
242
17
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).
 
Last edited:

chown33

Moderator
Staff member
Aug 9, 2009
8,356
4,330
Gourd City
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).
It's not an error. They're in two separate contexts/scopes.