MorpionView est sur l'Apple Store

Bonjour

Merci beaucoup pour cet automate
je vois que l’ IA passe la main au joueur au bout de 5s

Je n’ai qu’un seul joueur et l’IA
En fait il faudrait que je joueur ne puisse incrémenter que le compteur bleu
et que l’activation de l’IA se fasse automatiquement après que le joueur ai joué et incrémente le compteur vert (pas d’activation de l’iA par bouton)

il me reste le problème du passage du joueur à l’IA

Première chose : modifier le compteur pour qu’il puisse exécuter une action quand le joueur clique dessus.

Nouvelle version avec une closure :

struct CompteurJoueur:View {
  @Binding var compteur:Int
  @Binding var composantActif : Bool
  var couleur : Color
  var action: () -> Void
  var body : some View {
    Text(String(compteur))
      .font(.largeTitle)
      .bold()
      .foregroundColor(Color.white)
      .frame(width: 100, height: 100)
      .background(couleur)
      .onTapGesture {
        if self.composantActif {
          self.action()
        }
    }
  }
}

Utilisation :

      CompteurJoueur(compteur: $compteurBleu,
               composantActif: $interfaceActive,
               couleur: Color.blue,
               action: {
                  self.compteurBleu += 1
                  self.activerEtat(etat: .tourIA) })

Quand le joueur clique sur le compteur bleu, la closure action: incrémente le compteur et active l’état .tourIA.

Le compteur vert est une version simplifiée, juste un afficheur sans aucune interaction possible avec le joueur.

Il est incrémenté quand l’application passe en mode IA.

case .tourIA:
  couleurFond = Color.gray
  interfaceActive = false
  // Incrémentation compteur vert
  compteurVert += 1
  DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    self.activerEtat(etat: .tourJoueur)
  }

Le nouveau code complet, conformément à ton cahier de charges :

import SwiftUI

struct CompteurJoueur:View {
  @Binding var compteur:Int
  @Binding var composantActif : Bool
  var couleur : Color
  var action: () -> Void
  var body : some View {
    Text(String(compteur))
      .font(.largeTitle)
      .bold()
      .foregroundColor(Color.white)
      .frame(width: 100, height: 100)
      .background(couleur)
      .onTapGesture {
        if self.composantActif {
          self.action()
        }
    }
  }
}

struct CompteurIA:View {
  @Binding var compteur:Int
  var couleur : Color
  var body : some View {
    Text(String(compteur))
      .font(.largeTitle)
      .bold()
      .foregroundColor(Color.white)
      .frame(width: 100, height: 100)
      .background(couleur)
  }
}

enum EtatJeu : String {
  case indetermine    = "Indéterminé"
  case tourJoueur     = "Tour Joueur"
  case tourIA         = "Tour IA"
  case joueurGagnant  = "Joueur Gagnant"
  case joueurPerdant  = "Joueur Perdant"
}

struct ContentView: View {
    @State var etatJeu = EtatJeu.indetermine
    @State var interfaceActive = true
    @State var compteurBleu = 0
    @State var compteurVert = 0
    @State var couleurFond = Color.white
  
    var body: some View {
      VStack {
        Spacer()
        Text(etatJeu.rawValue)
          .font(.system(size: 50)).bold()
        Spacer()
        HStack {
          Spacer()
          CompteurJoueur(compteur: $compteurBleu,
                   composantActif: $interfaceActive,
                   couleur: Color.blue,
                   action: {
                      self.compteurBleu += 1
                      self.activerEtat(etat: .tourIA) })
          Spacer()
          CompteurIA(compteur: $compteurVert,
                     couleur: Color.green)
          Spacer()
        }
        Spacer()
        
      } .onAppear(
        perform: { self.activerEtat(etat: .tourJoueur)})
      .background(couleurFond)
  }
  
  func activerEtat(etat:EtatJeu) {
    switch etat {
    case .indetermine:
      break
    case .tourJoueur:
      interfaceActive = true
      couleurFond = Color.white
    case .tourIA:
      couleurFond = Color.gray
      interfaceActive = false
      // Incrémentation compteur vert
      compteurVert += 1
      DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
        self.activerEtat(etat: .tourJoueur)
      }
    case .joueurGagnant:
      interfaceActive = false
      // Faire d'autres choses ..
    case .joueurPerdant:
      interfaceActive = false
      // Faire d'autres choses ..
    }
    etatJeu = etat
  }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Génial avec l’utilisation de la closure action, c’est qui me manquait

j’ai juste déplacé l’incrementation de compteur vert dans le DispatchQueue pour que l’incrementation ne soit pas immédiate.
Cela fonctionne à merveille
Un grand merci d’avoir pris le temps de me donner tous ces conseils
cela donne des bons concepts de programmation

On peut changer l’ordre de démarrage IA/joueur avec un Toggle à la place de .onAppear
Toggle(isOn: $quiDemarre) {
if self.quiDemarre == false {
Text(« le joueur Commence »)
activerEtat(etat: .tourJoueur)
} else {
Text(« l’IA commence »)
activerEtat(etat: .tourIA)
}
}
Mais je ne peux pas utiliser activerEtat dans ce cas

Pas bête !
Tu peux aussi utiliser un délai aléatoire pour simuler un délai de réflexion variable.

case .tourIA:
  couleurFond = Color.gray
  interfaceActive = false
  let delai = Double.random(in: 0.1...1.5)
  DispatchQueue.main.asyncAfter(deadline: .now() + delai) {
    self.compteurVert += 1
    self.activerEtat(etat: .tourJoueur)
  }

Les closures sont des outils vraiment très puissants.

par contre pour Toggle çà ne fonctionne pas
je veux peux pas faire appel a une fonction dans le toggle

Toggle ne peut pas déclencher une action, sa seule fonction est d’inverser l’état d’une variable binaire.

Si tu veux modifier des paramètres du jeu avant la partie (qui commence la partie, et la taille de la grille), il faut un bouton pour démarrer le jeu en tenant compte de ces paramètres.

1 « J'aime »

ok merci
c’est un peu la difficulté de SwiftUI, on ne peut pas mettre du code ou l’on veut

Bonjour
voici un début avec un damier 3 * 3 en dur
est-ce que les choses sont au bon endroit ?

Tu peux augmenter la lisibilité et la présentation du code de plusieurs manières.

  1. Dés que le code dépasse deux lignes, il est préférable de le regrouper dans une fonction. Par exemple :

         Button(action:{
             self.damier.nouvelleGrille(nbLignes: 3, nbColonnes: 3)
             self.activerEtat(etat: .tourIA)
             self.compteurIA = 0
             self.compteurJoueur = 0
         }) {
             Text("IA Commence")
                 .font(.caption)
                 
                 .bold()
                 .foregroundColor(.white)
                 .padding()
         }
         .background(Color.red)
         .cornerRadius(10)
         .padding()
     }
    

Le bouton effectue deux choses : une réinitialisation du plateau de jeu et le démarrage en mode .tourIA. Le reset du jeu étant une fonction générique du jeu pouvant être utilisé à différents endroits du code, il est préférable de la traiter dans une fonction spécialisée.

func resetGrilleJeu() {
  self .damier.nouvelleGrille(nbLignes: 3, nbColonnes: 3)
  self.compteurIA = 0
  self.compteurJoueur = 0
}

L’action est plus lisible comme ça :

        Button(action:{
            self.resetGrilleJeu()
            self.activerEtat(etat: .tourIA)
        }) {
            Text("IA Commence")
                .font(.caption)
                
                .bold()
                .foregroundColor(.white)
                .padding()
        }
        .background(Color.red)
        .cornerRadius(10)
        .padding()
  1. De la même manière, pourquoi placer la description du bouton dans le code principal ? Surtout que tu utilises le même style de bouton a deux endroits dans le code. Il est préférable de créer un style de bouton personnalisé.

     struct BoutonPerso: View {
       var text:String
       var couleur : Color
       var body: some View {
         
         Text(text)
           .font(.caption)
           .bold()
           .foregroundColor(.white)
           .padding()
           .background(couleur)
           .cornerRadius(10)
           .padding()
       }
     }
    

C’est beaucoup plus lisible comme ça :

          Button(action:{
              self.resetGrilleJeu()
              self.activerEtat(etat: .tourIA)
          }) {
             BoutonPerso(text: "IA Commence", couleur: Color.red)
          }

Un grand merci
le code est facile à comprendre organisé de la sorte, pas de mélange de fonctions
Je n’avais pas pensé que le bouton entier était une vue

j’aurai bien aimé pousser la chose plus loin en mettant tout l’automate dans un fichier à part automate.swift: pas simple

enum EtatJeu: String {
case indetermine = « Indeterminé »
case tourJoueur = « Tour Joueur »
case tourIA = « Tour IA »
case joueurGagnant = « Joueur Gagnant »
case IAGagnant = « Joueur Perdant »
case pasDeGagnant = « Pas de Gagnant »
case finDujeu = « Fin du Jeu »
case reset = « Reset »
}

func activerEtat(etat: EtatJeu) {
switch etat {
case .indetermine:
break
case .tourJoueur:
interfaceActive = true
couleurFond = Color.white
boutonIAVisible = true

    case .tourIA:
        interfaceActive = false
        couleurFond = Color.gray
        boutonIAVisible = false
        
        // Increment Compteur Ordi
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.compteurIA += 1
            self.damier.ordiCase()

// if self.compteurIA == 5 || self.compteurJoueur == 5 {
if self.compteurIA == 5 {
self.activerEtat(etat: .finDujeu)
} else {
self.activerEtat(etat: .tourJoueur)
}
}
case .joueurGagnant:
interfaceActive = false
boutonIAVisible = false
case .IAGagnant:
interfaceActive = false
boutonIAVisible = false
case .reset:
interfaceActive = false
compteurIA = 0
compteurJoueur = 0
case .pasDeGagnant:
interfaceActive = false
case .finDujeu:
interfaceActive = false
}

    etatJeu = etat
}

que mettre dans GrilleView pour afficher une grille

struct GrilleView_Previews: PreviewProvider {

static var previews: some View {
    GrilleView(actif: <#T##Binding<Bool>#>, liste: <#T##[[DescriptionCase]]#>, damier: <#T##Damier#>, action: <#T##() -> Void#>)
}

}

Une application en trois fichiers :

moteurEtat

  • Deux images, trois états (changement de couleurs des images) et des boutons pour sélectionner les états.

Le contentView.swift :

import SwiftUI

struct ContentView: View {
    @State var machine = MachineEtats()
    
    var body: some View {
      VStack {
        Spacer()
        HStack {
          Spacer()
          FormView(description: machine.forme1)
          Spacer()
          FormView(description: machine.forme2)
          Spacer()
        }
        Spacer()
        VStack {
          Button(action: {
            self.machine.activer(etat: .etatBleu)
          }) {
              Text("Etat Bleu").font(.largeTitle)
          }
          Button(action: {
            self.machine.activer(etat: .etatRouge)
          }) {
            Text("Etat Rouge").font(.largeTitle)
          }
          Button(action: {
            self.machine.activer(etat: .etatVert)
          }) {
            Text("Etat vert").font(.largeTitle)
          }
        }
        Spacer()
      }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Un second fichier pour décrire l’objet FormView :

import SwiftUI

enum TypeForme : String {
  case cercle    = "circle"
  case rectangle = "rectangle"
  case triangle  = "triangle"
  case hexagone  = "hexagon"
  case ok        = "hand.raised"
}

struct DescriptionForm {
  var forme = TypeForme.ok
  var couleur = Color.blue
}

struct FormView: View {
    var description : DescriptionForm
    var body: some View {
      Image(systemName: description.forme.rawValue)
        .resizable()
        .frame(width: 130, height: 130)
        .foregroundColor(description.couleur)
        .shadow(radius: 10)
    }
}

struct FormView_Previews: PreviewProvider {
    static var previews: some View {
      FormView(description: DescriptionForm())
    }
}

Un troisième fichier contenant la machine à état :

import SwiftUI

enum EtatMachine {
  case etatNonDefini
  case etatBleu
  case etatRouge
  case etatVert
}

struct MachineEtats {
  
  // ------------------------------
  // Les variables d'états
  var forme1 = DescriptionForm()
  var forme2 = DescriptionForm()
  
  
  // -----------------------------
  // Mécanisme d'initialisation
  var etatCourant = EtatMachine.etatNonDefini
  
  // Activation de l'état de début
  init() {
    activer(etat: .etatBleu)
  }
  
  // ------------------------------
  // Définition des états
  mutating func activer(etat:EtatMachine) {
    switch (etat) {
    case .etatNonDefini:
      break
    case .etatBleu:
      forme1.couleur = Color.blue
      forme2.couleur = Color.blue
    case .etatVert:
      forme1.couleur = Color.green
      forme2.couleur = Color.green
    case .etatRouge:
      forme1.couleur = Color.red
      forme2.couleur = Color.red
    }
    etatCourant = etat
  }
  
}

Cela ne peut fonctionner que si le moteur d’état est une variable de type @State.

struct ContentView: View {
    @State var machine = MachineEtats()
    
    var body: some View {
       .....

Les propriétés des objets graphiques sont définis à partir de la machine à états :

FormView(description: machine.forme1)
FormView(description: machine.forme2)

EDIT : Je me compliquais vraiment la vie en tentant d’utiliser une classe Swift pour gérer certaines choses, comme la machine à état. Alors que c’est si simple avec les structures. SwiftUI c’est vraiment un nouveau monde. Il faut désapprendre les anciens réflexes de programmation pour exploiter pleinement ce framework.

Génial la machine à état qui renvoie un @State

J’ai essayé d’intégrer le nouvel automate à l’ancien projet
le joueur fonctionne correctement
pour l’iA l’écran n’est pas rafraichi

Ton application a du succès, déjà deux téléchargements … 3 avec le mien !

TROUVE ! En moins d’une minute … (coup de chance).
Cela ne risque pas de fonctionner car tu crées DEUX damiers, l’un dans le ContentView et l’autre dans l’automate. Le composant GrilleView étant lié avec le Damier de la ContentView, il ne peut refléter les modifications faites par l’IA dans le second Damier.

Mon exemple est un peu confus, parce qu’on ne pas faire de vrai @Binding entre les classes Swift et les structs SwftUI. Je vais refaire un code plus propre, sans classe Swift, uniquement avec des structs, en utilisant ce que j’ai appris ce WE. Ce sera plus simple et plus lisible.

sympa
Quand je la remettrai sur l’apple store je te citerai car c’est toi qui a fait travail de remise à plat
l’article sur le forum a également du succès 240 vues

Je pense que cela peut aider d’autres personnes que moi à bien coder

Déja, ça m’aide moi à mieux coder !

Ou apprends tu sur swift est-ce sur YouTube