Own "Circle The Dot" Game - weird behavior

Viewed 25

First things first: This probably isn't a quick-to-answer-question :(

If you have enough time - read on.

My little project

I want to recreate the Circle The Dot Game by Ketchapp in less than 500 Lines of code. You can try a replica online here. I implemented this using a 2D-Array of UIButtons. Here you can take a quick look of how it looks (the labels are for debugging) Screenshot of the game scene. I use this GitHub project for orientation (its Java Code).

Complete code

You can just download the whole project: DOWNLOAD PROJECT

Or copy the code: (just added a view called dotView in the Main.storyboard with measures: X=20, Y=296, Width = 394, Height 390.) Thats all:

    import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var dotView: UIView!
    
    var rowBtnArr: [UIButton] = [] // Simple 1D-Array of ALL Buttons
    var btnArr: [[UIButton]] = [] // 2D-Array with ALL Buttons
    let defaultColor = UIColor.lightGray
    let playerColor = UIColor.systemBlue
    let objColor = UIColor.orange
    var button: UIButton = UIButton()
    var playerX = 4
    var playerY = 4
    
    override func viewDidLoad() {
        super.viewDidLoad()
        buildBoard()
        btnArr = Utils.arrayToArrays(arr: rowBtnArr, size: 9)
          for btnline in btnArr{
            for btn in btnline{
                dotView.addSubview(btn)
            }
        }
        btnArr[playerX][playerY].backgroundColor = playerColor
        updateTitleColors()
    }

    @objc func buttonAction(sender: UIButton!) {
    
        print("Button " + sender.title(for: .normal)! + " tapped")
        if(sender.backgroundColor == defaultColor){
            btnArr[getX(btn: sender)][getY(btn: sender)].backgroundColor = objColor
            btnArr[playerX][playerY].backgroundColor = defaultColor
            oneStep()
        }
        //btnArr[playerX][playerY].backgroundColor = defaultColor
        //btnArr[playerX][playerY].backgroundColor = playerColor
        updateTitleColors()
    }
    
    func setupBtn( x: Int, y: Int)->UIButton{
        let btnSize = 40
        if(y%2==0){ //Gerade Zeilen normal
            button = UIButton(frame: CGRect(x: x*btnSize, y: y*btnSize, width: btnSize-3, height: btnSize-3))
        }else{ // Ungerade Zeilen versetzt
            button = UIButton(frame: CGRect(x: x*btnSize+20, y: y*btnSize, width: btnSize-3, height: btnSize-3))
        }
        button.layer.cornerRadius = 0.5 * button.bounds.size.width
        button.clipsToBounds = true
        button.backgroundColor = defaultColor
        button.setTitle(String(x) + " " + String(y), for: .normal)
        button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
        //button.setTitleColor(button.backgroundColor, for: .normal)
        return button
    }
    
    func buildBoard(){
        for i in 0...8{
            for j in 0...8{
                rowBtnArr.append(setupBtn(x: i, y: j))
            }
        }
    }
    func getXFromString(string: String) -> Int{ //"2 4"
        let separator = " "
        let newstr = string
        guard let x = newstr.components(separatedBy: separator).first else { return 99 }
        return Int(x)!
    }
    func getYFromString(string: String) -> Int{ //"2 4"
        let separator = " "
        let newstr = string
        guard let x = newstr.components(separatedBy: separator).last else { return 99 }
        return Int(x)!
    }
    func getX(btn: UIButton) -> Int{
        let separator = " "
        let newstr = btn.title(for: .normal)
        guard let x = newstr?.components(separatedBy: separator).first else { return 99 }
        return Int(x)!
    }
    func getY(btn: UIButton) -> Int{
        let separator = " "
        let newstr = btn.title(for: .normal)
        guard let y = newstr?.components(separatedBy: separator).last else { return 99 }
        return Int(y)!
    }
    
    func updateTitleColors(){
//        for btnline in btnArr{
//            for btn in btnline{
//                btn.setTitleColor(btn.backgroundColor, for: .normal)
//            }
//        }
    }
    
    
    func oneStep(){
        let point = String(playerX) + " " + String(playerY)
        if(isOnBorder(point: point)){
            playerX = -1
            playerY = -1
            // YOU LOST ENDSCREEN
            print("YOU LOST")
            reset()
            
        }else{
            let direction = findDirection()
            if(getXFromString(string: direction) == -1){
               // YOU WON
                print("YOU WON")
                reset()
            }else{
                playerX = getXFromString(string: direction)
                playerY = getYFromString(string: direction)
                btnArr[playerX][playerY].backgroundColor = playerColor
            }
        }
    }
    
    func findDirection()->String{
        var blocked: [String] = [] //Array enthält z.B. "3 5", "5 5"
        let myQueue = LinkedQueue<Pair>()
        let pair = Pair()
        var possibleNeighbours = findPossibleNeighbours(btn: btnArr[playerX][playerY], blockedArr: blocked)
        print(String(possibleNeighbours.description) + " possibeNeighs beginning" )
       possibleNeighbours.shuffle()
        
        for neighbour in possibleNeighbours{
            if(isOnBorder(point: neighbour)){
                print("Is on border")
                return neighbour
            }
            pair.setPair(firstValue: neighbour, secondValue: neighbour)
            myQueue.enqueue(value: pair)
            blocked.append(neighbour)
        }
        
        while(!myQueue.isEmpty){
            print("SCHLEIFE")
            print((myQueue.tail?.value.first)! + " " + (myQueue.tail?.value.second)!)
            let pointPair = myQueue.dequeue()
            possibleNeighbours = findPossibleNeighbours(btn: btnArr[getXFromString(string: (pointPair?.value.first)!)][getYFromString(string: (pointPair?.value.first)!)], blockedArr: blocked)
            for neighbour in possibleNeighbours{
                
                if isOnBorder(point: neighbour){
                    return (pointPair?.value.getSecond())!
                }
                pair.setPair(firstValue: neighbour, secondValue: (pointPair?.value.getSecond())!)
                myQueue.enqueue(value: pair)
                blocked.append(neighbour)
            }
        }
        return "-1 -1"
    }
    
    func isOnBorder(point: String)->Bool{
        return (getXFromString(string: point) == 0 || getXFromString(string: point) == 8 || getYFromString(string: point) == 0 || getYFromString(string: point) == 8)
    }
    
    func findPossibleNeighbours(btn: UIButton,blockedArr: [String])->[String]{
        var neighbours: [String] = []
        let x = getX(btn: btn)
        let y = getY(btn: btn)
        if(y%2==1){
            if(isFree(btn: btnArr[x][y-1])){ //OBEN
                let a = String(x) + " " + String(y-1)
                if(!blockedArr.contains(a)){
                    neighbours.append(a)
                }
            }
            if(isFree(btn: btnArr[x+1][y-1])){ //OBENRECHTS
                let b = String(x+1) + " " + String(y-1)
                if(!blockedArr.contains(b)){
                    neighbours.append(b)
                }
            }
            if(isFree(btn: btnArr[x+1][y])){ //RECHTS
                let c = String(x+1) + " " + String(y)
                if(!blockedArr.contains(c)){
                    neighbours.append(c)
                }
            }
            if(isFree(btn: btnArr[x+1][y+1])){ //UNTENRECHTS
                let d = String(x+1) + " " + String(y+1)
                if(!blockedArr.contains(d)){
                    neighbours.append(d)
                }
            }
            if(isFree(btn: btnArr[x][y+1])){ //UNTEN
                let e = String(x) + " " + String(y+1)
                if(!blockedArr.contains(e)){
                    neighbours.append(e)
                }
            }
            if(isFree(btn: btnArr[x-1][y])){ //LINKS
                let f = String(x-1) + " " + String(y)
                if(!blockedArr.contains(f)){
                    neighbours.append(f)
                }
            }
        }else{
            if(isFree(btn: btnArr[x][y-1])){ //OBEN
                let aa = String(x) + " " + String(y-1)
                if(!blockedArr.contains(aa)){
                    neighbours.append(aa)
                }
            }
            if(isFree(btn: btnArr[x+1][y])){ //RECHTS
                let bb = String(x+1) + " " + String(y)
                if(!blockedArr.contains(bb)){
                    neighbours.append(bb)
                }
            }
            if(isFree(btn: btnArr[x][y+1])){ //UNTEN
                let cc = String(x) + " " + String(y+1)
                if(!blockedArr.contains(cc)){
                    neighbours.append(cc)
                }
            }
            if(isFree(btn: btnArr[x-1][y+1])){ //UNTENLINKS
                let dd = String(x-1) + " " + String(y+1)
                if(!blockedArr.contains(dd)){
                    neighbours.append(dd)
                }
            }
            if(isFree(btn: btnArr[x-1][y])){ //LINKS
                let ee = String(x-1) + " " + String(y)
                if(!blockedArr.contains(ee)){
                    neighbours.append(ee)
                }
            }
            if(isFree(btn: btnArr[x-1][y-1])){ //OBENLINKS
                let ff = String(x-1) + " " + String(y-1)
                if(!blockedArr.contains(ff)){
                    neighbours.append(ff)
                }
            }
        }
        return neighbours
    }
    
    func isFree(btn: UIButton)->Bool{
        if(btn.backgroundColor == defaultColor){
            return true
        }
        return false
    }
    
    func reset(){
        playerX = 4
        playerY = 4
        for btnline in btnArr{
          for btn in btnline{
            btn.backgroundColor = defaultColor
          }
      }
        btnArr[4][4].backgroundColor = playerColor
    }
    
}

// (0,0) (1,0) (2,0) (3,0) (4,0) (5,0) (6,0) (7,0) (8,0)

// (0,1) (1,1) (2,1) (3,1) (4,1) (5,1) (6,1) (7,1) (8,1)

// (0,2) (1,2) (2,2) (3,2) (4,2) (5,2) (6,2) (7,2) (8,2)

// (0,3) (1,3) (2,3) (3,3) (4,3) (5,3) (6,3) (7,3) (8,3)

// (0,4) (1,4) (2,4) (3,4) (4,4) (5,4) (6,4) (7,4) (8,4)

// (0,5) (1,5) (2,5) (3,5) (4,5) (5,5) (6,5) (7,5) (8,5)

// (0,6) (1,6) (2,6) (3,6) (4,6) (5,6) (6,6) (7,6) (8,6)

// (0,7) (1,7) (2,7) (3,7) (4,7) (5,7) (6,7) (7,7) (8,7)

// (0,8) (1,8) (2,8) (3,8) (4,8) (5,8) (6,8) (7,8) (8,8)
// _____________________________________________________
// row1  row2  row3  row4  row5  row6  row7  row8  row9




//        for btnline in btnArr{
//            print("-----------")
//            for btn in btnline{
//            print(btn.title(for: .normal)!)
//            }
//        }
//____________________________________________________
class Node<T> {
  var value:T
  var next:Node?
  init(value:T) {
    self.value = value
  }
}
class LinkedQueue<T> {
  var tail:Node<T>?
  var head:Node<T>?
  var count:Int = 0
    var isEmpty: Bool{
        return (count == 0)
    }
  func dequeue () -> Node<T>? {
    if let node = head {
      head = head?.next
      count -= 1
        return node
    }
    return nil
  }
  func enqueue(value:T) {
    let newNode = Node(value:value)
    if let tailNode = tail {
      tailNode.next = newNode
      newNode.next = nil
      tail = newNode
    } else {
      head = newNode
      tail = newNode
    }
    count += 1
  }
}

        import Foundation
    class Utils{
    
        static func arrayToArrays<T>(arr: Array<T>,size: Int)->Array<Array<T>>{
            
            var result : Array<Array<T>> = Array<Array<T>>(repeating: Array<T>(), count: size);
    
            var set = -1;
            let expectedSize = (arr.count / size) + (arr.count % size);
    
            for i in 0..<arr.count {
                if i % expectedSize == 0{
                    set+=1;
                }
    
                result[set].append(arr[i]);
            }
    
            return result;
        }
    }
import Foundation

class Pair{
    var first: String = ""
    var second: String = ""
    
    func setPair(firstValue: String, secondValue: String){
        first = firstValue
        second = secondValue
    }
    func getFirst() -> String{
        return first
    }
    func getSecond() -> String{
        return second
    }
}

My Problem When I play it without removing the trail (Button stays orange when visited) it ALMOST works perfectly, you can test this by uncommenting line 41. But with removing the trail (Like original Circle the dot) it is very confusing. I tried to find the problem for multiple hours now - thats why I post everything here :/

When I click button (2,3) at the very beginning it stops working - found no way to the border, but I only blocked ONE button... I just makes no sense when you compare it to the findDirection method, which includes the Breadth-First-Search-Algorithm. Same thing with some other buttons. Its a little better when uncommenting line 135, which adds random to the game.

It would be sooo awesome if someone can help me or join the project. Its just for fun, I want to learn new things.

Greetings

SwiftHobby

0 Answers
Related