From 7c43d0d040611e688953d392e0ec5f0621ce6e56 Mon Sep 17 00:00:00 2001 From: Kevin Coughlin <706967+KevinTCoughlin@users.noreply.github.com> Date: Sat, 26 Oct 2024 14:16:21 -0700 Subject: [PATCH] Fix bug --- tictactoe Shared/GameScene.swift | 198 +++++++++++-------------------- 1 file changed, 70 insertions(+), 128 deletions(-) diff --git a/tictactoe Shared/GameScene.swift b/tictactoe Shared/GameScene.swift index 011428b..5cecd59 100644 --- a/tictactoe Shared/GameScene.swift +++ b/tictactoe Shared/GameScene.swift @@ -11,82 +11,55 @@ typealias GameColor = UIColor typealias GameColor = NSColor #endif -/// Main game scene for a Tic-Tac-Toe game using SpriteKit. class GameScene: SKScene { - // MARK: - Properties - - /// Size of the Tic-Tac-Toe board, configurable. - fileprivate var boardSize: Int = 3 - - /// 2D array to store each cell node on the board. + fileprivate var boardSize: Int = 3 // Default to 3x3 board, configurable fileprivate var board: [[SKSpriteNode?]] = [] - - /// Bit representation of X's moves. - fileprivate var xBoard: Int = 0 - - /// Bit representation of O's moves. - fileprivate var oBoard: Int = 0 - - /// Tracks the current player (X or O). + fileprivate var xBoard: Int = 0 // Bit representation of X's moves + fileprivate var oBoard: Int = 0 // Bit representation of O's moves fileprivate var currentPlayer: Player = .x - - /// Reference to the winning line to remove on reset. - fileprivate var winningLine: SKShapeNode? - - /// Tracks the current state of the game. - fileprivate var gameState: GameState = .ongoing - - /// Array of bit patterns representing all possible win conditions. - private var winningPatterns: [Int] = [] + fileprivate var winningLine: SKShapeNode? // To store the winning line + fileprivate var gameState: GameState = .ongoing // Track the current state of the game // MARK: - Enums - - /// Enum representing each player, X and O, with unique properties. + + /// Enumeration for players, representing X and O with unique properties. enum Player: Int { case x = 1, o - - /// The symbol used for the player (X or O). + var symbol: String { @inline(__always) get { ["❌", "⭕"][rawValue - 1] } } - - /// Boolean indicating if the player uses text-based rendering. + var isTextBased: Bool { @inline(__always) get { self == .x } } - - /// The color associated with the player (red for X, blue for O). + var fontColor: GameColor { @inline(__always) get { [GameColor.red, GameColor.blue][rawValue - 1] } } - - /// Returns the opposite player, to switch turns. + var next: Player { @inline(__always) get { self == .x ? .o : .x } } } - - /// Enum representing the game state, indicating if the game is ongoing, won, or a draw. + + /// Enumeration for the game state, indicating whether the game is ongoing, won, or a draw. enum GameState { case ongoing case won case draw } - - // MARK: - Scene Initialization - - /// Creates a new GameScene with a specified board size. - /// - /// - Parameter boardSize: The size of the Tic-Tac-Toe board (e.g., 3 for 3x3). - /// - Returns: A configured `GameScene` instance. + + private var winningPatterns: [Int] = [] // Store generated winning patterns based on board size + class func newGameScene(boardSize: Int = 3) -> GameScene { guard let scene = SKScene(fileNamed: "GameScene") as? GameScene else { print("Failed to load GameScene.sks") @@ -96,25 +69,18 @@ class GameScene: SKScene { scene.generateWinningPatterns() return scene } - - // MARK: - Touch Handling - + #if os(iOS) - /// Handles touch events for iOS by calculating the touch location. override func touchesBegan(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } handleTouch(at: touch.location(in: self)) } #elseif os(macOS) - /// Handles mouse click events for macOS by calculating the click location. override func mouseDown(with event: NSEvent) { handleTouch(at: event.location(in: self)) } #endif - - /// Determines which cell was tapped based on the location of the touch. - /// - /// - Parameter location: The CGPoint location of the touch or click in the scene. + private func handleTouch(at location: CGPoint) { let cellSize = min(size.width, size.height) / CGFloat(boardSize) let xOffset = -cellSize * CGFloat(boardSize) / 2 @@ -127,10 +93,11 @@ class GameScene: SKScene { makeMove(row: row, col: col) } } - - // MARK: - Board Setup and Reset - - /// Draws the board by creating cell nodes and positioning them. + + override func didMove(to view: SKView) { + drawBoard() + } + fileprivate func drawBoard() { let cellSize = min(size.width, size.height) / CGFloat(boardSize) let xOffset = -cellSize * CGFloat(boardSize) / 2 @@ -142,7 +109,7 @@ class GameScene: SKScene { for col in 0.. Int { + return 1 << (row * boardSize + col) + } + + private func generateWinningPatterns() { + winningPatterns.removeAll() + + // Row patterns + winningPatterns.append(contentsOf: (0.. Bool { + return winningPatterns.contains { (playerBoard & $0) == $0 } + } + + fileprivate func checkDraw() -> Bool { + let fullBoard = (1 << (boardSize * boardSize)) - 1 + return (xBoard | oBoard) == fullBoard + } + func makeMove(row: Int, col: Int) { // Prevent moves if the game is already won or drawn guard gameState == .ongoing else { return } @@ -223,9 +225,6 @@ class GameScene: SKScene { } } - /// Draws a line across the winning cells. - /// - /// - Parameter winningPattern: The bit pattern representing the winning line. fileprivate func drawWinningLine(for winningPattern: Int) { var winningCoordinates: [(Int, Int)] = [] @@ -256,68 +255,11 @@ class GameScene: SKScene { let line = SKShapeNode(path: path) line.strokeColor = currentPlayer.fontColor line.lineWidth = 10.0 - line.name = "winningLine" + line.name = "winningLine" // Tag the line for removal during reset + // Add line to the scene addChild(line) - winningLine = line - } - } - - // MARK: - Utility Functions - - /// Converts a row and column into a unique bit position. - /// - /// - Parameters: - /// - row: The row of the cell. - /// - col: The column of the cell. - /// - Returns: An integer with the bit set for the cell's position. - @inline(__always) - private func positionToBit(row: Int, col: Int) -> Int { - return 1 << (row * boardSize + col) - } - - /// Checks if the provided player board matches any winning pattern. - /// - /// - Parameter playerBoard: Bit representation of the current player’s board. - /// - Returns: `true` if the player has a winning pattern, `false` otherwise. - fileprivate func checkWin(for playerBoard: Int) -> Bool { - return winningPatterns.contains { (playerBoard & $0) == $0 } - } - - /// Checks if the board is completely filled, indicating a draw. - /// - /// - Returns: `true` if there are no empty spaces, `false` otherwise. - fileprivate func checkDraw() -> Bool { - let fullBoard = (1 << (boardSize * boardSize)) - 1 - return (xBoard | oBoard) == fullBoard - } - - /// Generates winning patterns for the current board size. - private func generateWinningPatterns() { - winningPatterns.removeAll() - - // Row patterns - winningPatterns.append(contentsOf: (0..