From 438f912b94ee88d75cb11dea1d47f860fd646c20 Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Tue, 10 Dec 2024 07:41:43 +0000 Subject: [PATCH] v1.0, it works andd so does difficluty settnigs sorryy for the typos im really sleep derpived right noew... --- .../project.pbxproj | 18 +- .../contents.xcworkspacedata | 0 .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 MineSwift/ContentView.swift | 100 ++++++++++ .../{MineSwift => }/MineSwift.entitlements | 0 MineSwift/MineSwift/ContentView.swift | 24 --- MineSwift/{MineSwift => }/MineSwiftApp.swift | 0 MineSwift/MinesweeperGame.swift | 172 ++++++++++++++++++ MineSwift/MinesweeperView.swift | 114 ++++++++++++ .../Preview Assets.xcassets/Contents.json | 0 .../MineSwiftTests.swift | 0 .../MineSwiftUITests.swift | 0 .../MineSwiftUITestsLaunchTests.swift | 0 15 files changed, 396 insertions(+), 32 deletions(-) rename {MineSwift/MineSwift.xcodeproj => MineSwift.xcodeproj}/project.pbxproj (98%) rename {MineSwift/MineSwift.xcodeproj => MineSwift.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) rename MineSwift/{MineSwift => }/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename MineSwift/{MineSwift => }/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename MineSwift/{MineSwift => }/Assets.xcassets/Contents.json (100%) create mode 100644 MineSwift/ContentView.swift rename MineSwift/{MineSwift => }/MineSwift.entitlements (100%) delete mode 100644 MineSwift/MineSwift/ContentView.swift rename MineSwift/{MineSwift => }/MineSwiftApp.swift (100%) create mode 100644 MineSwift/MinesweeperGame.swift create mode 100644 MineSwift/MinesweeperView.swift rename MineSwift/{MineSwift => }/Preview Content/Preview Assets.xcassets/Contents.json (100%) rename {MineSwift/MineSwiftTests => MineSwiftTests}/MineSwiftTests.swift (100%) rename {MineSwift/MineSwiftUITests => MineSwiftUITests}/MineSwiftUITests.swift (100%) rename {MineSwift/MineSwiftUITests => MineSwiftUITests}/MineSwiftUITestsLaunchTests.swift (100%) diff --git a/MineSwift/MineSwift.xcodeproj/project.pbxproj b/MineSwift.xcodeproj/project.pbxproj similarity index 98% rename from MineSwift/MineSwift.xcodeproj/project.pbxproj rename to MineSwift.xcodeproj/project.pbxproj index a9d6d92..2327490 100644 --- a/MineSwift/MineSwift.xcodeproj/project.pbxproj +++ b/MineSwift.xcodeproj/project.pbxproj @@ -406,18 +406,19 @@ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.1; + IPHONEOS_DEPLOYMENT_TARGET = 16; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 15.1; + MACOSX_DEPLOYMENT_TARGET = 13; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.neon443.MineSwift; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,7"; + TARGETED_DEVICE_FAMILY = "1,2"; XROS_DEPLOYMENT_TARGET = 2.1; }; name = Debug; @@ -445,18 +446,19 @@ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.1; + IPHONEOS_DEPLOYMENT_TARGET = 16; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 15.1; + MACOSX_DEPLOYMENT_TARGET = 13; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.neon443.MineSwift; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,7"; + TARGETED_DEVICE_FAMILY = "1,2"; XROS_DEPLOYMENT_TARGET = 2.1; }; name = Release; diff --git a/MineSwift/MineSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MineSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from MineSwift/MineSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to MineSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/MineSwift/MineSwift/Assets.xcassets/AccentColor.colorset/Contents.json b/MineSwift/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from MineSwift/MineSwift/Assets.xcassets/AccentColor.colorset/Contents.json rename to MineSwift/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/MineSwift/MineSwift/Assets.xcassets/AppIcon.appiconset/Contents.json b/MineSwift/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from MineSwift/MineSwift/Assets.xcassets/AppIcon.appiconset/Contents.json rename to MineSwift/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/MineSwift/MineSwift/Assets.xcassets/Contents.json b/MineSwift/Assets.xcassets/Contents.json similarity index 100% rename from MineSwift/MineSwift/Assets.xcassets/Contents.json rename to MineSwift/Assets.xcassets/Contents.json diff --git a/MineSwift/ContentView.swift b/MineSwift/ContentView.swift new file mode 100644 index 0000000..761f843 --- /dev/null +++ b/MineSwift/ContentView.swift @@ -0,0 +1,100 @@ +// +// ContentView.swift +// MineSwift +// +// Created by Nihaal Sharma on 08/12/2024. +// + +import SwiftUI + +struct ContentView: View { + @State private var cRows: Int = 10 + @State private var cCols: Int = 10 + @State private var cBombs: Int = 10 + @State private var selectedMode: MinesweeperMode = .beginner + + var body: some View { + let selectedGrid = MinesweeperGrid(mode: selectedMode, cRows: cRows, cCols: cCols, cBombs: cBombs) + + NavigationStack { + VStack { + List { + Section("Game Settings") { + Text(selectedMode == .custom ? "Custom Game" : "Preset Difficulty") + .font(.system(.title2, design: .rounded, weight: .heavy)) + ModePicker(selectedMode: $selectedMode) + Text(selectedGrid.describe()) + + if selectedMode == .custom { + CustomSettingsView(cRows: $cRows, cCols: $cCols, cBombs: $cBombs, maxBombs: cRows * cCols) + .animation(.spring, value: selectedMode) + } + } + + Section("How to play") { + Text("1. Select difficulty or choose a custom grid") + Text("2. Tap Start Game") + Text("3. Tap a square to reveal if it is a mine") + Text("4. If it isnt a mine, it will display the number of mines in a 1 square radius, including diagonally") + Text("5. Use the flag switch to place flags on squares") + Text("Try to find all the squares that are not bombs.") + Text("Good Luck!") + } + } + + Spacer() + + if cBombs > cRows * cCols { + List { + Text("More bombs than squares!") + Text("You can have a max of \(cRows*cCols) bombs.") + } + } + + NavigationLink(destination: MinesweeperView(rows: selectedGrid.rows, cols: selectedGrid.cols, bombs: selectedGrid.bombs)) { + Text("Start Game") + .font(.system(.title, weight: .bold)) + .foregroundStyle(.white) + .padding(10) + .background(cBombs > cRows * cCols ? .red : .blue) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .padding(.bottom) + } + } + .navigationTitle("MineSwift") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } + } +} + +struct ModePicker: View { + @Binding var selectedMode: MinesweeperMode + + var body: some View { + Picker("Mode", selection: $selectedMode) { + ForEach(MinesweeperMode.allCases, id: \.self) { mode in + Text(mode.rawValue).tag(mode) + } + } + .pickerStyle(.segmented) + } +} + +struct CustomSettingsView: View { + @Binding var cRows: Int + @Binding var cCols: Int + @Binding var cBombs: Int + let maxBombs: Int + + var body: some View { + Stepper("Rows: \(cRows)", value: $cRows, in: 1...30) + Stepper("Columns: \(cCols)", value: $cCols, in: 1...30) + Stepper("Bombs: \(cBombs)", value: $cBombs, in: 1...maxBombs) + } +} + +#Preview { + ContentView() +} diff --git a/MineSwift/MineSwift/MineSwift.entitlements b/MineSwift/MineSwift.entitlements similarity index 100% rename from MineSwift/MineSwift/MineSwift.entitlements rename to MineSwift/MineSwift.entitlements diff --git a/MineSwift/MineSwift/ContentView.swift b/MineSwift/MineSwift/ContentView.swift deleted file mode 100644 index f83c1a0..0000000 --- a/MineSwift/MineSwift/ContentView.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ContentView.swift -// MineSwift -// -// Created by Nihaal Sharma on 08/12/2024. -// - -import SwiftUI - -struct ContentView: View { - var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") - } - .padding() - } -} - -#Preview { - ContentView() -} diff --git a/MineSwift/MineSwift/MineSwiftApp.swift b/MineSwift/MineSwiftApp.swift similarity index 100% rename from MineSwift/MineSwift/MineSwiftApp.swift rename to MineSwift/MineSwiftApp.swift diff --git a/MineSwift/MinesweeperGame.swift b/MineSwift/MinesweeperGame.swift new file mode 100644 index 0000000..ab7bc6b --- /dev/null +++ b/MineSwift/MinesweeperGame.swift @@ -0,0 +1,172 @@ +// +// MinesweeperGame.swift +// MineSwift +// +// Created by Nihaal Sharma on 08/12/2024. +// + +import Foundation + +enum MinesweeperMode: String, CaseIterable { + case beginner, intermediate, expert, custom + + var details: (rows: Int, cols: Int, bombs: Int) { + switch self { + case .beginner: return (10, 10, 10) + case .intermediate: return (16, 16, 40) + case .expert: return (30, 16, 99) + case .custom: return (20, 20, 10) + } + } +} + +struct MinesweeperGrid { + let rows: Int + let cols: Int + let bombs: Int + + init(mode: MinesweeperMode, cRows: Int = 20, cCols: Int = 20, cBombs: Int = 10) { + switch mode { + case .beginner, .intermediate, .expert: + let details = mode.details + self.rows = details.rows + self.cols = details.cols + self.bombs = details.bombs + case .custom: + self.rows = cRows + self.cols = cCols + self.bombs = cBombs + } + } + + func describe() -> String { + return "\(rows)x\(cols) minefield with \(bombs) mines." + } +} + +class MinesweeperGame: ObservableObject { + enum CellState { + case hidden, revealed, flagged + } + + struct Cell { + var isMine: Bool = false + var adjacentMines: Int = 0 + var state: CellState = .hidden + } + + struct Position: Hashable { + var row: Int + var col: Int + } + + private var rows: Int + private var columns: Int + @Published var board: [[Cell]] + @Published var gameOver: Bool = false + @Published var gameWon: Bool = false + @Published var revealedCells = Set() + + init(rows: Int = 10, columns: Int = 10, mineCount: Int = 10) { + self.rows = rows + self.columns = columns + self.board = Array(repeating: Array(repeating: Cell(), count: columns), count: rows) + placeMines(mineCount) + calculateAdjacentMines() + } + + private func placeMines(_ mineCount: Int) { + var minesPlaced = 0 + while minesPlaced < mineCount { + let row = Int.random(in: 0..= 0 && newRow < rows && newCol >= 0 && newCol < columns { + if board[newRow][newCol].isMine { + adjacentMines += 1 + } + } + } + } + board[row][col].adjacentMines = adjacentMines + } + } + } + + func revealCell(row: Int, col: Int) { + guard !gameOver else { return } + + var stack: [(Int, Int)] = [(row, col)] + var revealedCellsLocal = Set() + + while !stack.isEmpty { + let (r, c) = stack.removeLast() + let pos = Position(row: r, col: c) + if revealedCellsLocal.contains(pos) { continue } + revealedCellsLocal.insert(pos) + + if board[r][c].isMine { + gameOver = true + revealAllBombs() + return + } + board[r][c].state = .revealed + revealedCells.insert(pos) + + if board[r][c].adjacentMines == 0 { + for dr in -1...1 { + for dc in -1...1 { + if dr == 0 && dc == 0 { continue } + let newRow = r + dr + let newCol = c + dc + if newRow >= 0 && newRow < rows && newCol >= 0 && newCol < columns { + if !revealedCellsLocal.contains(Position(row: newRow, col: newCol)) { + stack.append((newRow, newCol)) + } + } + } + } + } + } + + func revealAllBombs() { + for row in 0.. 0 { + Text("\(cell.adjacentMines)") + .foregroundStyle(.white) + .bold() + } + } + + if cell.state == .flagged { + Image(systemName: "flag.fill") + .resizable() + .scaledToFit() + .frame(width: 20) + .foregroundStyle(.yellow) + } + } + } +} + +#Preview { + MinesweeperView(rows: 10, cols: 10, bombs: 5) +} + diff --git a/MineSwift/MineSwift/Preview Content/Preview Assets.xcassets/Contents.json b/MineSwift/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from MineSwift/MineSwift/Preview Content/Preview Assets.xcassets/Contents.json rename to MineSwift/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/MineSwift/MineSwiftTests/MineSwiftTests.swift b/MineSwiftTests/MineSwiftTests.swift similarity index 100% rename from MineSwift/MineSwiftTests/MineSwiftTests.swift rename to MineSwiftTests/MineSwiftTests.swift diff --git a/MineSwift/MineSwiftUITests/MineSwiftUITests.swift b/MineSwiftUITests/MineSwiftUITests.swift similarity index 100% rename from MineSwift/MineSwiftUITests/MineSwiftUITests.swift rename to MineSwiftUITests/MineSwiftUITests.swift diff --git a/MineSwift/MineSwiftUITests/MineSwiftUITestsLaunchTests.swift b/MineSwiftUITests/MineSwiftUITestsLaunchTests.swift similarity index 100% rename from MineSwift/MineSwiftUITests/MineSwiftUITestsLaunchTests.swift rename to MineSwiftUITests/MineSwiftUITestsLaunchTests.swift