Développement d'un bot Awale pour Slack
Nouveau challenge pour cette semaine : le développement du jeu Awalé avec l’ajout d’une intelligence artificielle. Il n'est plus question d'un affichage dans le terminal comme la première semaine d'intégration, il doit être jouable dans Slack à travers un bot.
Création du bot slack ?
Non, nous n'allons pas commencer directement par la création du bot, il faut avancer progressivement, étape par étape.
Architecture du projet
Contrairement au projet python, je n'ai pas réussi à mettre en place Docker (docker-compose), le temps imparti était trop court, je me suis retrouvé bloqué lors de l'installation de PostgreSQL.
Comme vous pouvez le voir, il s'agit d'une architecture web classique faisant intervenir un serveur HTTP (nginx), un serveur php-fpm (ici en version 7.0) et une base de données. Nous avons aussi notre application en GO qui contient l'IA et le jeu en console.
Il m’a fallu mettre en place Ngrok qui permet d'exposer un serveur local directement sur un internet à travers un tunnel sécurisé. Pour le lancer rien de plus simple, make expose
va générer une url d'accès sur le web.
Intelligence artificielle
Dans le premier billet sur l'Awale, le jeu a été crée en Python. L'objectif ici est de réaliser une IA; pour cela il a été décidé de refaire le jeu en GO.
Mais pourquoi avoir choisi ce langage ? Il apporte de la performance, les channels et goroutines, qui permettent de rendre une partie de l'algorithme asynchrone. Enfin, ce fut pour moi la découverte d'un nouveau langage.
Le jeu a été entièrement refait, diverses améliorations ont été apportées :
- L'interface graphique a été revue
- Le choix de l'opposant a été introduite (Joueur ou IA)
L'implémentation de l'IA dans le projet exige de mettre en place l'algorithme MiniMax qui permet de déterminer le meilleur trou à jouer en fonction d'un scoring des possibilités.
Domaine complètement nouveau pour moi, on ne peut pas dire que l’IA soit très compétente, mais elle donne tout de même la possibilité au joueur d’effectuer une partie sans un autre joueur. La fonction scoring d'une board fut bien plus complexe à développer que ce à quoi je m'attendais. J’espère que ma prochaine IA rendra la vie plus difficile aux joueurs !
J’ai dû mettre en place un serveur web, afin que différents outils puissent appeler l'IA et les fonctions du jeu.
Un exemple d'une fonction du serveur web en GO :
package main
import (
"net/http"
"encoding/json"
"game"
"constants"
"player"
)
func newGame(w http.ResponseWriter, r *http.Request) {
playerOne := player.New(0, true, constants.PIT_COUNT)
playerTwo := player.New(1, false, constants.PIT_COUNT)
currentGame := game.New([]player.Player{playerOne, playerTwo})
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(currentGame)
}
func main() {
http.HandleFunc("/new", newGame)
http.ListenAndServe(":2000", nil)
}
Les sources du projet son disponibles sur Github marmelab/awale-go.
Serveur de bot sous Symfony
La seconde partie du projet est la mise en place de Symfony avec une base de données. Plusieurs choix s'offrent à moi pour la réalisation de cette partie.
J'aurais pu coder le jeu entièrement en PHP et appeler l'IA qui me renvoie simplement la position choisie. Malheureusement, le temps pour réaliser ce projet était trop court, de plus je n'aurais pas utilisé totalement le développement réalisé en GO.
Pour faire simple, j'envoie au serveur GO une requête POST qui contient un plateau de jeu un score et une position. Il me répond avec une image du jeu au format JSON (la nouvelle board, les nouveaux score et le statut de la partie). GO contient toute la logique du jeu, au final je développe très peu de code en PHP.
Afin de pouvoir faire ces appels, je dois sauvegarder les informations de la partie côté Symfony. Pour cela j'utilise une base de données sous PostgreSQL qui contient simplement trois colonnes.
Pour un schéma de données aussi petit, l'utilisation d'un ORM comme Doctrine est le choix le plus logique. Avec deux commandes on peut générer notre base de données et nos Getters/Setters.
php bin/console doctrine:database:create
php bin/console doctrine:generate:entities AppBundle/Entity/Game
Symfony va me servir aussi à concevoir une image du plateau de jeu, j'utilise une librairie externe, Intervention Image.
Fini la console !
Les webhooks Slack
Il existe plusieurs façons d'utiliser l'API de slack.
- Incoming webhooks permet d'envoyer des messages
- Slack commands : donne la possibilité d'écouter une commande que l'on paramètre et d'obtenir son résultat (Ne peut pas envoyer une image en réponse)
- Bot users : permet de tout faire, utiliser les web sokets pour exécuter les différentes actions
- Outgoing webhooks : ressemble très fortement à Slack commands mais avec des limitations (1 message par seconde, seulement sur les channels public)
Pour faire le bon choix parmi cette liste, il faut déjà définir ce que l'on veut obtenir.
L'utilisateur doit saisir : /awale new
, /awale restart
, /awale 1
, /awale 2
, /awale position
.
Pour cela, j'ai choisi d'utiliser le Slack commands qui va écouter /awale et Incoming webhooks pour répondre au message de l'utilisateur. Je n'ai pas choisi d'utiliser le Bot Users à cause des web sockets, que je ne maîtrise pas en PHP. Une application en NodeJs ou GO serait plus pertinente.
La documentation et les exemples sur le site sont très explicites. J'aime beaucoup utiliser les services externes dans mes différentes projets, je n'avais pas encore joué avec l'API de slack. Maintenant c’est chose faite !
Gestion de projet
Pendant cette semaine, j'ai travaillé avec Jonathan. Il m'a donné quelques conseils sur la création de tâche dans Trello. J'ai rencontré plus de difficultés à découper mes tâches en utilisant les principes vus dans le dernier article sur l'Awalé. J'ai principalement des tâches techniques avec la mise en place de différent outils, l'architecture, les tests, etc... tout cela ne parle pas au client.
Comme on peut le voir sur l'image, j'ai ajouté les tests d'acceptance dans la description des tâches avec une todo list purement technique. On peut très bien faire une tâche utilisateur avec une partie technique grâce à cette technique.
Conclusion
Le début de la nouvelle semaine d'intégration est encore plus soutenu. J'ai perdu du temps sur la mise en place de Docker et les différentes erreurs sur PostgreSQL. On a décidé de retirer cette contrainte pour avancer plus rapidement.
Par ailleurs, le projet se termine plutôt bien, je suis plutôt satisfait du rendu du jeu.
Je perçois toujours autant d'entraide entre les différents membres de l'équipe, ils sont curieux de ce que je fais et n’hésitent pas à venir me voir. Ce sont des conditions de travail que j’apprécie, et dans lesquelles j’aime évoluer.
Prochaine étape, réalisation du jeu en React Native.
Le code source du jeu est disponible sur GitHub marmelab/awale-symfony.