diff --git a/App/App.xcodeproj/project.pbxproj b/App/App.xcodeproj/project.pbxproj index f5a5422..9875b09 100644 --- a/App/App.xcodeproj/project.pbxproj +++ b/App/App.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ F0F59E4C2DD4958800BE32D6 /* C4Rules.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E442DD492B400BE32D6 /* C4Rules.xcframework */; }; F0F59E4F2DD4996F00BE32D6 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = F0F59E4E2DD4996F00BE32D6 /* Localizable.xcstrings */; }; F0F59E512DD49C2800BE32D6 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F0F59E502DD49C2800BE32D6 /* Colors.xcassets */; }; + F0F59E532DDDC35100BE32D6 /* NewGameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F59E522DDDC35100BE32D6 /* NewGameView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -57,6 +58,7 @@ F0F59E442DD492B400BE32D6 /* C4Rules.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4Rules.xcframework; path = ../precompiled/xcframeworks/C4Rules.xcframework; sourceTree = ""; }; F0F59E4E2DD4996F00BE32D6 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = App/Localizable.xcstrings; sourceTree = ""; }; F0F59E502DD49C2800BE32D6 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + F0F59E522DDDC35100BE32D6 /* NewGameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewGameView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -163,6 +165,7 @@ isa = PBXGroup; children = ( F001A04D2DD48FAB00809561 /* MainMenuView.swift */, + F0F59E522DDDC35100BE32D6 /* NewGameView.swift */, ); path = View; sourceTree = ""; @@ -302,6 +305,7 @@ files = ( F001A04E2DD48FAB00809561 /* MainMenuView.swift in Sources */, F001A04C2DD48FAB00809561 /* AppApp.swift in Sources */, + F0F59E532DDDC35100BE32D6 /* NewGameView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/App/App/Localizable.xcstrings b/App/App/Localizable.xcstrings index fe16ed7..a614b0a 100644 --- a/App/App/Localizable.xcstrings +++ b/App/App/Localizable.xcstrings @@ -35,6 +35,125 @@ } } }, + "generic.player.type.aiFinnishHim" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "(AI) Finnish Him" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "(IA) Finnish Him" + } + } + } + }, + "generic.player.type.aiRandom" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "(AI) Random" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "(IA) Aléatoire" + } + } + } + }, + "generic.player.type.aiSimpleNegaMax" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "(AI) Simple NegaMax" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "(IA) NegaMax Simple" + } + } + } + }, + "generic.player.type.human" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Human" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Humain" + } + } + } + }, + "generic.player1.name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player 1" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Joueur 1" + } + } + } + }, + "generic.player2.name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player 2" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Joueur 2" + } + } + } + }, + "generic.rules.classic.name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classic" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classique" + } + } + } + }, "mainMenu.button.newGame" : { "extractionState" : "manual", "localizations" : { @@ -68,6 +187,173 @@ } } } + }, + "newGame.dimensions" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensions" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensions" + } + } + } + }, + "newGame.dimensions.alignedTokens %llu" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tokens to align: %llu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jetons à aligner: %llu" + } + } + } + }, + "newGame.dimensions.height %llu" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%llu rows" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%llu lignes" + } + } + } + }, + "newGame.dimensions.width %llu" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%llu columns" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%llu colonnes" + } + } + } + }, + "newGame.play" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jouer" + } + } + } + }, + "newGame.player.name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom" + } + } + } + }, + "newGame.player.photo.picker" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Photo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Photo" + } + } + } + }, + "newGame.player.type" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Joueur" + } + } + } + }, + "newGame.rules.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rules" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Règles" + } + } + } + }, + "newGame.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New Game" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nouvelle partie" + } + } + } } }, "version" : "1.0" diff --git a/App/App/View/NewGameView.swift b/App/App/View/NewGameView.swift new file mode 100644 index 0000000..b2f2113 --- /dev/null +++ b/App/App/View/NewGameView.swift @@ -0,0 +1,100 @@ +// +// NewGameView.swift +// App +// +// Created by etudiant2 on 21/05/2025. +// + +import SwiftUI +import PhotosUI + +struct NewGameView: View { + @State private var p2: PlayerType = .AISimpleNegaMax + @State private var rules_type: RulesType = .Classic + @State private var width: UInt = 7 + @State private var height: UInt = 7 + @State private var alignedTokens: UInt = 4 + + var body: some View { + VStack { + Form { + Section("generic.player1.name") { + PlayerSectionView(initialType: .Human) + } + + Section("generic.player2.name") { + PlayerSectionView(initialType: .AISimpleNegaMax) + } + + HStack { + Picker("newGame.rules.title", systemImage: "globe", selection: $rules_type) { + Text("generic.rules.classic.name").tag(RulesType.Classic) + } + + Image(systemName: "globe") + } + + Section("newGame.dimensions") { + Stepper("newGame.dimensions.width \(width)", value: $width) + Stepper("newGame.dimensions.height \(height)", value: $height) + Stepper("newGame.dimensions.alignedTokens \(alignedTokens)", value: $alignedTokens) + } + + Button("newGame.play") { + // TODO: yes + } + } + } + } +} + +private struct PlayerSectionView: View { + @State private var type: PlayerType + @State private var name: String + @State private var photo: PhotosPickerItem? = nil + + var body: some View { + Picker("newGame.player.type", selection: $type) { + Text("generic.player.type.human").tag(PlayerType.Human) + Text("generic.player.type.aiRandom").tag(PlayerType.AIRandom) + Text("generic.player.type.aiFinnishHim").tag(PlayerType.AIFinnishHim) + Text("generic.player.type.aiSimpleNegaMax").tag(PlayerType.AISimpleNegaMax) + } + + let binding: Binding = if (self.type == .Human) { + $name + } else { + // FIXME: make text field readonly + .constant("TODO constant name") + } + TextField("newGame.player.name", text: binding) + // TODO: MacOS + //.textInputSuggestions(isEnabled: true) { + // + //} + + // TODO: actual photo support + Image(systemName: "globe").overlay { + PhotosPicker(selection: $photo) { + Text("newGame.player.photo.picker") + } + } + } + + init(initialType type: PlayerType) { + self.type = type + self.name = "TODO default name" + } +} + +private enum PlayerType { + case Human, AIRandom, AIFinnishHim, AISimpleNegaMax +} + +private enum RulesType { + case Classic +} + +#Preview { + NewGameView() +}