1. Avant de commencer
Les avancées spectaculaires d'AlphaGo et d'AlphaStar ont démontré le potentiel du machine learning pour créer des agents de jeu de niveau surhumain. Créer un petit jeu basé sur le ML est un exercice amusant pour acquérir les compétences nécessaires à la création d'agents de jeu puissants.
Dans cet atelier de programmation, vous allez apprendre à créer un jeu de société à l'aide des éléments suivants :
- TensorFlow Agent pour entraîner un agent de jeu avec l'apprentissage par renforcement
- TensorFlow Serving pour diffuser le modèle
- Flutter pour créer une application de jeu de société multiplate-forme
Prérequis
- Connaissances de base du développement Flutter avec Dart
- Connaissances de base du machine learning avec TensorFlow, telles que l'entraînement par rapport au déploiement
- Connaissances de base de Python, des terminaux et de Docker
Points abordés
- Entraîner un agent de personnage non-joueur (PNJ) à l'aide de TensorFlow Agents
- Diffuser le modèle entraîné à l'aide de TensorFlow Serving
- Créer un jeu de société multiplate-forme avec Flutter
Prérequis
- SDK Flutter
- Android et iOS configurés pour Flutter
- Configuration Desktop pour Flutter
- Configuration Web pour Flutter
- Visual Studio Code (VS Code) configuré pour Flutter et Dart
- Docker
- Bash
- Python 3.7 ou version ultérieure
2. The Plane Strike Game
Le jeu que vous allez créer dans cet atelier de programmation s'appelle "Plane Strike". Il s'agit d'un petit jeu de société à deux joueurs qui ressemble au jeu de société Battleship. Les règles sont très simples :
- Le joueur humain affronte un agent PNJ entraîné par le machine learning. Le joueur humain peut commencer la partie en appuyant sur n'importe quelle cellule du plateau de l'agent.
- Au début du jeu, le joueur humain et l'agent disposent chacun d'un objet "avion" (huit cellules vertes formant un "avion", comme vous pouvez le voir sur le plateau du joueur humain dans l'animation ci-dessous) sur leur propre plateau. Ces "avions" sont placés de manière aléatoire et ne sont visibles que par les propriétaires du plateau, et non par leurs adversaires.
- Le joueur humain et l'agent frappent à tour de rôle une cellule du plateau de l'autre. Le joueur humain peut appuyer sur n'importe quelle cellule du plateau de l'agent, tandis que l'agent fera automatiquement son choix en fonction de la prédiction d'un modèle de machine learning. La cellule tentée devient rouge s'il s'agit d'une cellule "avion" ("touché"), sinon elle devient jaune ("manqué").
- Le premier joueur à obtenir huit cellules rouges remporte la partie, qui est ensuite relancée avec de nouveaux plateaux.
Voici un exemple de gameplay :

3. Configurer votre environnement de développement Flutter
Pour le développement Flutter, vous avez besoin de deux logiciels pour réaliser cet atelier de programmation : le SDK Flutter et un éditeur.
Vous pouvez exécuter l'atelier de programmation sur l'un des appareils suivants :
- Le simulateur iOS (les outils Xcode doivent être installés)
- Android Emulator (à configurer dans Android Studio)
- Un navigateur (Chrome est requis pour le débogage)
- En tant qu'application de bureau Windows, Linux ou macOS. Vous devez développer votre application sur la plate-forme où vous comptez la déployer. Par exemple, si vous voulez développer une application de bureau Windows, vous devez le faire sous Windows pour accéder à la chaîne de compilation appropriée. Les exigences spécifiques aux systèmes d'exploitation sont détaillées sur docs.flutter.dev/desktop.
4. Configuration
Pour télécharger le code de cet atelier de programmation, procédez comme suit :
- Accédez au dépôt GitHub pour cet atelier de programmation.
- Cliquez sur Code > Download ZIP (Code > Télécharger le fichier ZIP) afin de télécharger l'ensemble du code pour cet atelier de programmation.

- Décompressez le fichier ZIP téléchargé pour accéder au dossier racine
codelabs-maincontenant toutes les ressources nécessaires.
Pour cet atelier de programmation, vous n'avez besoin que des fichiers du sous-répertoire tfagents-flutter/ dans le dépôt, qui contient plusieurs dossiers :
- Les dossiers
step0àstep6contiennent le code de démarrage sur lequel s'appuie chaque étape de cet atelier de programmation. - Le dossier
finishedcontient le code final de l'application exemple. - Chaque dossier contient un sous-dossier
backbend, qui inclut le code de backend, et un sous-dossierfrontend, qui inclut le code de frontend Flutter.
5. Télécharger les dépendances pour le projet
Backend
Ouvrez votre terminal et accédez au sous-dossier tfagents-flutter. Exécutez la commande suivante :
pip install -r requirements.txt
Interface
- Dans VS Code, cliquez sur File > Open folder (Fichier > Ouvrir le dossier), puis sélectionnez le dossier
step0dans le code source que vous avez téléchargé précédemment. - Ouvrir le fichier
step0/frontend/lib/main.dart. Si une boîte de dialogue VS Code vous invite à télécharger les packages requis pour l'application de démarrage, cliquez sur Get packages (Télécharger les packages). - Si cette boîte de dialogue ne s'affiche pas, ouvrez votre terminal, puis exécutez la commande
flutter pub getdans le dossierstep0/frontend.

6. Étape 0 : Exécuter l'application de départ
- Ouvrez le fichier
step0/frontend/lib/main.dartdans VS Code, assurez-vous qu'Android Emulator ou le simulateur iOS est correctement configuré et s'affiche dans la barre d'état.
Par exemple, voici ce que vous voyez lorsque vous utilisez le Pixel 5 avec Android Emulator :

Voici ce qui s'affiche lorsque vous utilisez l'iPhone 13 avec le simulateur iOS :

- Cliquez sur l'icône Démarrer le débogage
.
Exécuter et explorer l'application
L'application devrait se lancer sur Android Emulator ou le simulateur iOS. L'UI est assez simple. Il existe deux plateaux de jeu. Un joueur humain peut appuyer sur n'importe quelle cellule du plateau de l'agent en haut de l'écran pour indiquer une position de frappe. Vous allez entraîner un agent intelligent à prédire automatiquement où frapper en fonction du plateau du joueur humain.
En arrière-plan, l'application Flutter envoie le plateau de jeu actuel du joueur humain au backend, qui exécute un modèle d'apprentissage par renforcement et renvoie une position de cellule prédite à frapper ensuite. Une fois la réponse reçue, le frontend affichera le résultat dans l'UI.

Si vous cliquez sur une cellule du tableau de l'agent maintenant, rien ne se passe, car l'application ne peut pas encore communiquer avec le backend.
7. Étape 1 : Créez un environnement Python TensorFlow Agents
L'objectif principal de cet atelier de programmation est de concevoir un agent qui apprend en interagissant avec un environnement. Bien que le jeu Plane Strike soit relativement simple et qu'il soit possible de créer manuellement des règles pour l'agent PNJ, vous utilisez l'apprentissage par renforcement pour entraîner un agent afin d'acquérir les compétences nécessaires et de pouvoir créer facilement des agents pour d'autres jeux à l'avenir.
Dans le paramètre standard de l'apprentissage par renforcement (RL), l'agent reçoit une observation à chaque étape et choisit une action. L'action est appliquée à l'environnement, qui renvoie une récompense et une nouvelle observation. L'agent entraîne une stratégie pour choisir des actions afin de maximiser la somme des récompenses, également appelée "retour". En jouant de nombreuses fois, l'agent est capable d'apprendre les schémas et d'affûter ses compétences pour maîtriser le jeu. Pour formuler le jeu Plane Strike comme un problème de RL, considérez l'état du plateau comme l'observation, une position de frappe comme l'action et le signal de touche/manque comme la récompense.

Pour entraîner l'agent PNJ, vous utilisez TensorFlow Agents, une bibliothèque d'apprentissage par renforcement fiable, évolutive et facile à utiliser pour TensorFlow.
TF-Agents est idéal pour l'apprentissage par renforcement, car il est fourni avec un ensemble complet d'ateliers de programmation, d'exemples et une documentation détaillée pour vous aider à vous lancer. Vous pouvez utiliser TF-Agents pour résoudre des problèmes de RL réalistes et complexes avec une grande évolutivité, et développer rapidement de nouveaux algorithmes de RL. Vous pouvez facilement passer d'un agent ou d'un algorithme à un autre pour effectuer des tests. Il est également bien testé et facile à configurer.
De nombreux environnements de jeu prédéfinis sont implémentés dans OpenAI Gym (par exemple, les jeux Atari), Mujuco, etc., que TF-Agents peut facilement exploiter. Toutefois, comme le jeu Plane Strike est un jeu personnalisé complet, vous devez d'abord implémenter un nouvel environnement à partir de zéro.
Pour implémenter un environnement Python TF-Agents, vous devez implémenter les méthodes suivantes :
class YourGameEnv(py_environment.PyEnvironment):
def __init__(self):
"""Initialize environment."""
def action_spec(self):
"""Return action_spec."""
def observation_spec(self):
"""Return observation_spec."""
def _reset(self):
"""Return initial_time_step."""
def _step(self, action):
"""Apply action and return new time_step."""
La plus importante est la fonction _step(), qui accepte une action et renvoie un nouvel objet time_step. Dans le cas du jeu Plane Strike, vous disposez d'un plateau de jeu. Lorsqu'une nouvelle position de frappe arrive, l'environnement détermine, en fonction de l'état du plateau de jeu :
- À quoi le plateau de jeu devrait-il ressembler ensuite (la cellule doit-elle changer de couleur pour devenir rouge ou jaune, compte tenu de l'emplacement de l'avion caché) ?
- Quelle récompense le joueur doit-il recevoir pour cette position (récompense pour un coup réussi ou pénalité pour un coup manqué) ?
- La partie doit-elle se terminer (quelqu'un a-t-il gagné) ?
- Ajoutez le code suivant à la fonction
_step()du fichier_planestrike_py_environment.py:
if self._hit_count == self._plane_size:
self._episode_ended = True
return self.reset()
if self._strike_count + 1 == self._max_steps:
self.reset()
return ts.termination(
np.array(self._visible_board, dtype=np.float32), UNFINISHED_GAME_REWARD
)
self._strike_count += 1
action_x = action // self._board_size
action_y = action % self._board_size
# Hit
if self._hidden_board[action_x][action_y] == HIDDEN_BOARD_CELL_OCCUPIED:
# Non-repeat move
if self._visible_board[action_x][action_y] == VISIBLE_BOARD_CELL_UNTRIED:
self._hit_count += 1
self._visible_board[action_x][action_y] = VISIBLE_BOARD_CELL_HIT
# Successful strike
if self._hit_count == self._plane_size:
# Game finished
self._episode_ended = True
return ts.termination(
np.array(self._visible_board, dtype=np.float32),
FINISHED_GAME_REWARD,
)
else:
self._episode_ended = False
return ts.transition(
np.array(self._visible_board, dtype=np.float32),
HIT_REWARD,
self._discount,
)
# Repeat strike
else:
self._episode_ended = False
return ts.transition(
np.array(self._visible_board, dtype=np.float32),
REPEAT_STRIKE_REWARD,
self._discount,
)
# Miss
else:
# Unsuccessful strike
self._episode_ended = False
self._visible_board[action_x][action_y] = VISIBLE_BOARD_CELL_MISS
return ts.transition(
np.array(self._visible_board, dtype=np.float32),
MISS_REWARD,
self._discount,
8. Étape 2 : Entraîner l'agent de jeu avec TensorFlow Agents
Une fois l'environnement TF-Agents en place, vous pouvez entraîner l'agent de jeu. Pour cet atelier de programmation, vous utilisez un agent REINFORCE. REINFORCE est un algorithme de gradient de stratégie dans l'apprentissage par renforcement. L'idée de base est d'ajuster les paramètres du réseau de neurones de la stratégie en fonction des signaux de récompense collectés pendant le jeu, afin que le réseau de stratégie puisse maximiser le retour lors des prochaines parties.
- Tout d'abord, vous devez instancier les environnements d'entraînement et d'évaluation. Ajoutez ce code à la fonction
train_agent()dans le fichierstep2/backend/training.py:
train_py_env = planestrike_py_environment.PlaneStrikePyEnvironment(
board_size=BOARD_SIZE, discount=DISCOUNT, max_steps=BOARD_SIZE**2
)
eval_py_env = planestrike_py_environment.PlaneStrikePyEnvironment(
board_size=BOARD_SIZE, discount=DISCOUNT, max_steps=BOARD_SIZE**2
)
train_env = tf_py_environment.TFPyEnvironment(train_py_env)
eval_env = tf_py_environment.TFPyEnvironment(eval_py_env)
- Ensuite, vous devez créer un agent d'apprentissage par renforcement qui sera entraîné. Dans cet atelier de programmation, vous allez utiliser l'agent REINFORCE, qui est un agent basé sur une stratégie. Ajoutez ce code juste en dessous du code ci-dessus :
actor_net = tfa.networks.Sequential(
[
tfa.keras_layers.InnerReshape([BOARD_SIZE, BOARD_SIZE], [BOARD_SIZE**2]),
tf.keras.layers.Dense(FC_LAYER_PARAMS, activation="relu"),
tf.keras.layers.Dense(BOARD_SIZE**2),
tf.keras.layers.Lambda(lambda t: tfp.distributions.Categorical(logits=t)),
],
input_spec=train_py_env.observation_spec(),
)
optimizer = tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE)
train_step_counter = tf.Variable(0)
tf_agent = reinforce_agent.ReinforceAgent(
train_env.time_step_spec(),
train_env.action_spec(),
actor_network=actor_net,
optimizer=optimizer,
normalize_returns=True,
train_step_counter=train_step_counter,
)
- Enfin, entraînez l'agent dans la boucle d'entraînement. Dans la boucle, vous collectez d'abord quelques épisodes de parties dans un tampon, puis vous entraînez l'agent avec les données mises en mémoire tampon. Ajoutez ce code à la fonction
train_agent()dans le fichierstep2/backend/training.py:
# Collect a few episodes using collect_policy and save to the replay buffer.
collect_episode(
train_py_env,
collect_policy,
COLLECT_EPISODES_PER_ITERATION,
replay_buffer_observer,
)
# Use data from the buffer and update the agent's network.
iterator = iter(replay_buffer.as_dataset(sample_batch_size=1))
trajectories, _ = next(iterator)
tf_agent.train(experience=trajectories)
replay_buffer.clear()
- Vous pouvez maintenant lancer l'entraînement. Dans votre terminal, accédez au dossier
step2/backendsur votre ordinateur et exécutez la commande suivante :
python training.py
L'entraînement prend entre 8 et 12 heures, selon la configuration matérielle (vous n'avez pas besoin de terminer l'entraînement vous-même, car un modèle préentraîné est fourni dans step3). En attendant, vous pouvez suivre la progression avec TensorBoard. Ouvrez un nouveau terminal, accédez au dossier step2/backend sur votre ordinateur et exécutez la commande suivante :
tensorboard --logdir tf_agents_log/
tf_agents_log est le dossier qui contient le journal d'entraînement. Voici un exemple d'exécution d'entraînement :
Vous pouvez constater que la durée moyenne des épisodes diminue et que le rendement moyen augmente à mesure que l'entraînement progresse. Intuitivement, vous pouvez comprendre que si l'agent est plus intelligent et fait de meilleures prédictions, la durée du jeu devient plus courte et l'agent collecte plus de récompenses. Cela est logique, car l'agent souhaite terminer le jeu en moins d'étapes pour minimiser la forte réduction de la récompense lors des dernières étapes.
Une fois l'entraînement terminé, le modèle entraîné est exporté vers le dossier policy_model.
9. Étape 3 : Déployez le modèle entraîné avec TensorFlow Serving
Maintenant que vous avez entraîné l'agent de jeu, vous pouvez le déployer avec TensorFlow Serving.
- Dans votre terminal, accédez au dossier
step3/backendsur votre ordinateur et démarrez TensorFlow Serving avec Docker :
docker run -t --rm -p 8501:8501 -p 8500:8500 -v "$(pwd)/backend/policy_model:/models/policy_model" -e MODEL_NAME=policy_model tensorflow/serving
Docker commence par télécharger automatiquement l'image TensorFlow Serving, ce qui prend une minute. Le service TensorFlow Serving devrait alors démarrer. Le journal doit se présenter comme cet extrait de code :
2022-05-30 02:38:54.147771: I tensorflow_serving/model_servers/server.cc:89] Building single TensorFlow model file config: model_name: policy_model model_base_path: /models/policy_model
2022-05-30 02:38:54.148222: I tensorflow_serving/model_servers/server_core.cc:465] Adding/updating models.
2022-05-30 02:38:54.148273: I tensorflow_serving/model_servers/server_core.cc:591] (Re-)adding model: policy_model
2022-05-30 02:38:54.262684: I tensorflow_serving/core/basic_manager.cc:740] Successfully reserved resources to load servable {name: policy_model version: 123}
2022-05-30 02:38:54.262768: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: policy_model version: 123}
2022-05-30 02:38:54.262787: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: policy_model version: 123}
2022-05-30 02:38:54.265010: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:38] Reading SavedModel from: /models/policy_model/123
2022-05-30 02:38:54.277811: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:90] Reading meta graph with tags { serve }
2022-05-30 02:38:54.278116: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:132] Reading SavedModel debug info (if present) from: /models/policy_model/123
2022-05-30 02:38:54.280229: I external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2022-05-30 02:38:54.332352: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle.
2022-05-30 02:38:54.337000: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2193480000 Hz
2022-05-30 02:38:54.402803: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/policy_model/123
2022-05-30 02:38:54.410707: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 145695 microseconds.
2022-05-30 02:38:54.412726: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/policy_model/123/assets.extra/tf_serving_warmup_requests
2022-05-30 02:38:54.417277: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: policy_model version: 123}
2022-05-30 02:38:54.419846: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models
2022-05-30 02:38:54.420066: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled
2022-05-30 02:38:54.428339: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ...
[warn] getaddrinfo: address family for nodename not supported
2022-05-30 02:38:54.431620: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ...
[evhttp_server.cc : 245] NET_LOG: Entering the event loop ...
Vous pouvez envoyer un exemple de requête au point de terminaison pour vous assurer qu'il fonctionne comme prévu :
curl -d '{"signature_name":"action","instances":[{"0/discount":0.0,"0/observation":[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]],"0/reward":0.0,"0/step_type":0}]}' -X POST http://localhost:8501/v1/models/policy_model:predict
Le point de terminaison renverra une position prédite 45, qui est (5, 5) au centre du plateau (pour les curieux, vous pouvez essayer de comprendre pourquoi le centre du plateau est une bonne estimation pour la première position de frappe).
{
"predictions": [45]
}
Et voilà ! Vous avez créé un backend pour prédire la prochaine position de frappe de l'agent PNJ.
10. Étape 4 : Créer l'application Flutter pour Android et iOS
Le backend est prêt. Vous pouvez commencer à lui envoyer des requêtes pour récupérer les prédictions de position de frappe à partir de l'application Flutter.
- Vous devez d'abord définir une classe qui encapsule les entrées à envoyer. Ajoutez ce code au fichier
step4/frontend/lib/game_agent.dart:
class Inputs {
final List<double> _boardState;
Inputs(this._boardState);
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['0/discount'] = [0.0];
data['0/observation'] = [_boardState];
data['0/reward'] = [0.0];
data['0/step_type'] = [0];
return data;
}
}
Vous pouvez maintenant envoyer la requête à TensorFlow Serving pour effectuer des prédictions.
- Ajoutez ce code à la fonction
predict()dans le fichierstep4/frontend/lib/game_agent.dart:
var flattenedBoardState = boardState.expand((i) => i).toList();
final response = await http.post(
Uri.parse('http://$server:8501/v1/models/policy_model:predict'),
body: jsonEncode(<String, dynamic>{
'signature_name': 'action',
'instances': [Inputs(flattenedBoardState)]
}),
);
if (response.statusCode == 200) {
var output = List<int>.from(
jsonDecode(response.body)['predictions'] as List<dynamic>);
return output[0];
} else {
throw Exception('Error response');
}
Une fois que l'application reçoit la réponse du backend, vous mettez à jour l'UI du jeu pour refléter la progression du jeu.
- Ajoutez ce code à la fonction
_gridItemTapped()dans le fichierstep4/frontend/lib/main.dart:
int agentAction =
await _policyGradientAgent.predict(_playerVisibleBoardState);
_agentActionX = agentAction ~/ _boardSize;
_agentActionY = agentAction % _boardSize;
if (_playerHiddenBoardState[_agentActionX][_agentActionY] ==
hiddenBoardCellOccupied) {
// Non-repeat move
if (_playerVisibleBoardState[_agentActionX][_agentActionY] ==
visibleBoardCellUntried) {
_agentHitCount++;
}
_playerVisibleBoardState[_agentActionX][_agentActionY] =
visibleBoardCellHit;
} else {
_playerVisibleBoardState[_agentActionX][_agentActionY] =
visibleBoardCellMiss;
}
setState(() {});
Exécuter l'application
- Cliquez sur l'icône Démarrer le débogage
, puis attendez que l'application se charge. - Appuyez sur n'importe quelle cellule du plateau de l'agent pour commencer la partie.

11. Étape 5 : Activez l'application Flutter pour les plates-formes de bureau
En plus d'Android et d'iOS, Flutter est également compatible avec les plates-formes de bureau, y compris Linux, Mac et Windows.
Linux
- Assurez-vous que l'appareil cible est défini sur
dans la barre d'état de VSCode. - Cliquez sur l'icône Démarrer le débogage
, puis attendez que l'application se charge. - Cliquez sur n'importe quelle cellule du plateau de l'agent pour commencer la partie.

Mac
- Pour Mac, vous devez configurer les droits d'accès appropriés, car l'application envoie des requêtes HTTP au backend. Pour en savoir plus, consultez Droits d'accès et bac à sable de l'application.
Ajoutez ce code à step4/frontend/macOS/Runner/DebugProfile.entitlements et step4/frontend/macOS/Runner/Release.entitlements, respectivement :
<key>com.apple.security.network.client</key>
<true/>
- Assurez-vous que l'appareil cible est défini sur
dans la barre d'état de VSCode. - Cliquez sur l'icône Démarrer le débogage
, puis attendez que l'application se charge. - Cliquez sur n'importe quelle cellule du plateau de l'agent pour commencer la partie.

Windows
- Assurez-vous que l'appareil cible est défini sur
dans la barre d'état de VSCode. - Cliquez sur l'icône Démarrer le débogage
, puis attendez que l'application se charge. - Cliquez sur n'importe quelle cellule du plateau de l'agent pour commencer la partie.

12. Étape 6 : Activez l'application Flutter pour la plate-forme Web
Vous pouvez également ajouter une compatibilité Web à l'application Flutter. Par défaut, la plate-forme Web est automatiquement activée pour les applications Flutter. Il vous suffit donc de la lancer.
- Assurez-vous que l'appareil cible est défini sur
dans la barre d'état de VSCode. - Cliquez sur l'icône Démarrer le débogage
, puis attendez que l'application se charge dans le navigateur Chrome. - Cliquez sur n'importe quelle cellule du plateau de l'agent pour commencer la partie.

13. Félicitations
Vous avez créé une application de jeu de société avec un agent optimisé par le ML pour jouer contre le joueur humain.
