1

I checked the existing posts on this topic and also googled it, but I am not able to identify my mistake or make this work for me. I have a function iterativeDeepening() inside the class ChessPlayer. After say 15 seconds I want to stop further iterations within the function. In the code below, the function "flagSetter" is never invoked. If I use NSTimer.fire() the function is invoked immediately and not after 15 seconds. I tried placing the flagSetter function before or after iterativeDeepening(). Either case does not work. What have I done incorrectly?

class ChessPlayer {
    var timeoutFlag = false
    //Code

    func iterativeDeepening() {

        ***//variables and constants***

        let timer = NSTimer.scheduledTimerWithTimeInterval(15.0, target: self, selector: #selector(self.flagSetter), userInfo: nil, repeats: false)

        ***while minDepth <= maxDepth
        {
            // Loop iteration code
            if timeoutFlag { break out of loop }
        }***

    }

    @objc func flagSetter(timer: NSTimer) {
        print("flag changed to true")
        self.timeoutFlag = true
        timer.invalidate()
    }
}

The requirement:

  1. computerThinking() is fired from GameScene from human move's action completion handler.
  2. GameScene.computerThinking() invokes ChessPlayer.iterativeDeepening()
  3. iterativeDeepening runs a while loop incrementing "depth". For each "depth" an optimal move at that depth is evaluated. Higher the depth, more detailed the evaluation.
  4. after 15.0 seconds i want to break out of the while loop with the depth and optimal move available at that point of time.
ArtBajji
  • 949
  • 6
  • 14
  • You say “I want to stop further iterations within the function” but there is nothing inside the function except the statement that creates the timer. What iterations are you talking about? – rob mayoff Apr 20 '16 at 01:56
  • I think your error is in the `repeats: false`. Change that to `repeats: true`. Otherwise the computer will only use it once and think you mean for it never to be used again, preventing success. – owlswipe Apr 20 '16 at 01:57
  • Hi. I have added details to the pseudocode and marked the changes in bold and italics. The changes are displayed between ***. The while loop runs the iterations until maxDepth is reached. If timeOut flag is set to true before any existing iteration, the loop breaks. To set the timeOut flag I need the timer to fire. Hope it helps. – ArtBajji Apr 20 '16 at 06:14
  • Hi. I tried a new solution changing the timer to repeat itself every 5 seconds I fired it manually for the first time. Due to character constraint I am providing it in 3 comments. See code below. self.timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: #selector(self.flagSetter), userInfo: nil, repeats: true) self.timer.fire() – ArtBajji Apr 20 '16 at 07:23
  • Also changed the function flagSetter as below. @objc func flagSetter() { if self.timeoutFlag { self.timeoutFlag = false print("flag changed to false") } else { self.timeoutFlag = true self.timer.invalidate() print("flag changed to true and invalidated") } } – ArtBajji Apr 20 '16 at 07:23
  • The idea is to initiate the timer and use the timeOutFlag as a toggle to invalidate the timer after the first repetition at 5 seconds interval. The manual firing of timer(flagSetter) works. But the repetition call is not happening. Does this help? Thanks. – ArtBajji Apr 20 '16 at 07:23
  • Wait, ArtBajji, are you just trying to call the function flagSetter 15 seconds after iterativeDeepening is called? Or is there more to this? PLEASE tell us exactly what you want to achieve and then we can help you!! – owlswipe Apr 20 '16 at 22:07
  • Iterative deepening works like this. At depth 2, computer analyses and obtains a best move. Now the depth is incremented to 3. Using the previous iteration's data, at the new depth again a new best move is analysed and obtained. The process continues for depth 4, 5, 6. At the end of 15 seconds, I want to break out of this loop with the best move at that point of time. So I am setting a flag, timeOutFlag in flagSetter function and calling it from scheduledTimerWithTimeInterval using #selector after 15 seconds. But the selector does not fire at all, unless fired manually. – ArtBajji Apr 21 '16 at 12:56
  • Had a minor progress. TimeOutFlag was initially false. In flagSetter I have set it to true. In iterativeDeepening, if TimeOutFlag becomes true, I would break out of loop. But it never happened. Outside the loop, if TimeOutFlag never became true, I invalidated timer. When I commented this, I saw that the flagSetter was fired after the loop iterations were completed. So, flagSetter did not get queued concurrently. But it got queued serially and executed only after the completion of loop. The flagSetter must fire automatically in 15.0 seconds from the time of creation of timer. – ArtBajji Apr 22 '16 at 17:12
  • I changed my approach and did away with scheduling timers and used NSDate, NSCalendar and date components. Here is the new approach. 1. identify start time before loop. 2. inside loop after playing computer move, calculate time elapsed from start time. 3. If elapsed time exceeds 15.0 seconds break out of the loop. I don't know why scheduling timers did not work in this case. But this new approach worked for me. Thanks a lot for all you inputs. Wish you all good luck. – ArtBajji Apr 23 '16 at 08:28

2 Answers2

0

Here is your solution: define timer outside of the function so you can invalidate it from another function. Right now, your timer is defined inside a function so it can only be altered inside that function, but that is not what you want. Fix it by doing the following: right below var timeoutFlag = false put var timer = NSTimer(). Then inside your function iterativeDeepening() get rid of the let. Then it will all work!!


Here's what your code should be, adapted from Hasya's answer and your provided code.

class ChessPlayer {
// Declare timer and timeoutFlag
var timer = NSTimer()   
var timeoutFlag = false

func iterativeDeepening() {

self.timer = NSTimer.scheduledTimerWithTimeInterval(15.0, target: self, selector: “timerEventOccured”, userInfo: nil, repeats: true)
}

func timerEventOccured() {
        print("timerEventOccured was called.")
        timeoutFlag = true
        self.timer.invalidate()
    }

}

override func viewDidUnload() {
super.viewDidUnload()
self.timer.invalidate()
}
}
owlswipe
  • 19,159
  • 9
  • 37
  • 82
  • Hi. Thanks. I implemented your suggestion. But I still have the same issue. Also improvised and tried a few variations of your suggestion. But the issue still persists. – ArtBajji Apr 20 '16 at 06:50
  • @ArtBajji Tell us what exactly you want this code to achieve, then post all your updated code (with the changes I mentioned above). Also, please set your timer to `repeats: true` instead of `repeats: false` and try running that. – owlswipe Apr 20 '16 at 20:23
  • @ArtBajji You must define `timer` up above. Once you do that, make see if it works and update the exact code you used. – owlswipe Apr 20 '16 at 20:32
  • Hi. I tried that too. Kindly check my last comment under prettyitgirl.com's solution. I have listed a set of options I tried, including this one. Also check my last comment under my question. It details the requirement completely. I am also going to append the detailed requirement under the question, below an <***asterick line***>. Hope that helps. – ArtBajji Apr 22 '16 at 04:28
0

I am a lover of Objective-c and never used Swift yet in my projects. Googling NSTimer Swift, I found out the following steps to implement NSTimer correctly.

we need to define our NSTimer. The first variable that we are going to need is a variable called timer of type NSTimer. We do this like:

var timer = NSTimer()

Start NSTimer timer:

timer = NSTimer.scheduledTimerWithTimeInterval(15.0, target:self, selector:#selector(ChessPlayer.flagSetter(_:)), userInfo: nil, repeats: false)

And your method flagSetter should be defined as this:

func flagSetter(timer: NSTimer) {
        print("flag changed to true")
        self.timeoutFlag = true
        timer.invalidate()
}

This will now surely work as I've made my very first app, just for this question, made in Swift. Check how I put my selector. You're right with the warnings by the way.

If you need more information about Selectors, check this thread: @selector() in Swift?

Community
  • 1
  • 1
Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
  • 1
    Hi. I have used a constant instead of a variable, hence the "let". Secondly, the usage of "Selector" constructor resulted in a warning. Hence I have replaced it with #selector(self.flagSetter) as suggested by latest updated Xcode. Finally as the function is defined inside a class, Xcode again issued a warning and suggested that @objc be added. Hope this helps. Thanks. – ArtBajji Apr 20 '16 at 06:21
  • Thanks for the info too. What happens if you continue to build/run even with the said warnings? I'll continue searching for this until you have your answer. – Glenn Posadas Apr 20 '16 at 07:23
  • If I build/run with warning, the issue is still the same. flagSetter is not fired. If I issue a manual fire using timer.fire(), then I get the following error. ChessaissanceV2[12016:1179587] *** NSForwarding: warning: object 0x7c89a1b0 of class 'ChessaissanceV2.ChessPlayer' does not implement methodSignatureForSelector: -- trouble ahead Unrecognized selector -[ChessaissanceV2.ChessPlayer flagSetter] – ArtBajji Apr 20 '16 at 07:30
  • Hi. Thanks for the link. It was informative. I have added the timer and selector exactly as it recommends. Still the issue persists. There is something amiss eluding me here. If I manually fire, the same selector fires and displays output. So I assume the selector and the timer are created correctly. But when I schedule it, it doesn't invoke the selector. There seems to be some issue with the scheduling part. – ArtBajji Apr 20 '16 at 09:03
  • You are not passing anything to your selector. Check my codes above how I implemented my selector. selector:#selector(ChessPlayer.flagSetter(_:)) ... I made a sample app in Swift and it works fine. – Glenn Posadas Apr 20 '16 at 09:06
  • Thanks. But it does not work for me. I tried combinations of one or more of the following. 1. timer as a global variable. 2. timer as class attribute. 3. flag setter as global function. 4. flag setter as class member function. 5. timeoutflag as global variable 6. timeoutflag as class attribute. 5. Selector Constructor. 6. #selector 7. selector with parameter. 8. selector without parameter. 9. flag setter with parameter. 10. flag setter without parameter. 11. firing timer manually once to see if it gets scheduled. 12. scheduling timer repeatedly. 13. scheduling timer once. – ArtBajji Apr 20 '16 at 09:21