Outils pour utilisateurs

Outils du site


jeu_de_la_vie

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
jeu_de_la_vie [2019/10/07 08:38] – [Qu'est-ce que le jeu de la vie?] Camillejeu_de_la_vie [2020/10/29 14:03] (Version actuelle) – ↷ Liens modifiés en raison d'un déplacement. serge
Ligne 1: Ligne 1:
-====== **Documentation du projet "Jeu de la vie" réalisé pour Centre Sciences en 2019** ======+====== Documentation du projet "Jeu de la vie" réalisé pour Centre Sciences en 2019 ======
  
 ===== Introduction: ===== ===== Introduction: =====
  
 ==== Cadre ==== ==== Cadre ====
-L'organisme Centre Sciences met en place une exposition sur l'informatique visant à faire comprendre quelques principes qui sous-tendent "l'intelligence artificielle". Trois réalisations ont été faites par Labomedia: le Jeu de la vie, le Casque de réalité virtuelle et l'installation Captcha. +L'organisme [[http://www.centre-sciences.org/|CentreSciences]] met en place une exposition sur l'informatique visant à faire comprendre quelques principes qui sous-tendent "l'intelligence artificielle". Trois réalisations ont été faites par Labomedia : le Jeu de la vie, le Casque de réalité virtuelle et l'installation Captcha. 
-==== Qu'est-ce que le jeu de la vie? ==== + 
-D'après Wikipedia +==== Qu'est-ce que le jeu de la vie ? ==== 
-//Le jeu de la vie est un automate cellulaire imaginé par John Horton Conway en 1970 et qui est probablement le plus connu de tous les automates cellulaires.  +D'après Wikipedia 
-Le jeu de la vie n’est pas un jeu, puisqu'il ne nécessite aucun joueur. Il s’agit d’un automate cellulaire, un modèle où chaque état conduit mécaniquement à l’état suivant à partir de règles pré-établies.+ 
 +Le jeu de la vie est un automate cellulaire imaginé par John Horton Conway en 1970 et qui est probablement le plus connu de tous les automates cellulaires.  
 +Le jeu de la vie n’est pas un jeu, puisqu'il ne nécessite aucun joueur. **Il s’agit d’un automate cellulaire**, un modèle où chaque état conduit mécaniquement à l’état suivant à partir de règles pré-établies.
  
 Le « jeu » se déroule sur une grille à deux dimensions, théoriquement infinie (mais de longueur et de largeur finies et plus ou moins grandes dans la pratique), dont les cases — qu’on appelle des « cellules », par analogie avec les cellules vivantes — peuvent prendre deux états distincts : « vivante » ou « morte ». Le « jeu » se déroule sur une grille à deux dimensions, théoriquement infinie (mais de longueur et de largeur finies et plus ou moins grandes dans la pratique), dont les cases — qu’on appelle des « cellules », par analogie avec les cellules vivantes — peuvent prendre deux états distincts : « vivante » ou « morte ».
  
-À chaque étape, l’évolution d’une cellule est entièrement déterminée par l’état de ses huit voisines de la façon suivante :  \\+**À chaque étape, l’évolution d’une cellule est entièrement déterminée par l’état de ses huit voisines** de la façon suivante : 
 + 
 +  *  Une cellule morte possédant exactement trois voisines vivantes devient vivante (elle naît). 
 +  *  Une cellule vivante possédant deux ou trois voisines vivantes le reste, sinon elle meurt. 
 + 
 +**La configuration de départ détermine entièrement l'évolution future**. Il existe des formes remarquables qui donnent naissance à des évolutions particulières: on a par exemple des formes périodiques qui vont faire alterner deux configurations.
  
-  *  Une cellule morte possédant exactement trois voisines vivantes devient vivante (elle naît).\\ 
-  *  
-  *  Une cellule vivante possédant deux ou trois voisines vivantes le reste, sinon elle meurt.\\ // 
-  *  
-La configuration de départ détermine entièrement l'évolution future. Il existe des formes remarquables qui donnent naissance à des évolutions particulières: on a par exemple des formes périodiques qui vont faire alterner deux configurations. 
 =====Cahier des charges===== =====Cahier des charges=====
-Dans le cadre d'une exposition, il était nécessaire d'imaginer un dispositif interactif que les visiteurs pouvaient manipuler. On s'est donc orienté vers une installation composée d'un plateau de jeu permettant de matérialiser la configuration initiale et un écran qui montrerait l'évolution. Cette évolution serait activée par l'appui sur un bouton ce qui laisse le temps au visiteur d'observer les figures générées et même d'anticiper la génération suivante.+Dans le cadre d'une exposition, il était nécessaire d'imaginer un dispositif interactif que les visiteurs pourraient manipuler. On s'est donc orienté vers une installation composée d'un plateau de jeu permettant de matérialiser la configuration initiale et un écran qui montrerait l'évolution. Cette évolution serait activée par l'appui sur un bouton ce qui laisse le temps au visiteur d'observer les figures générées et même d'anticiper la génération suivante.
 Le tout devait être assez compact et transportable. Le tout devait être assez compact et transportable.
 +
 =====Réalisation===== =====Réalisation=====
-Les choix techniques ont été les suivants: +Les choix techniques ont été les suivants:\\ 
-la contrainte d'encombrement réduit et de puissance de calcul suffisant a conduit au choix d'une RaspberryPi pour la partie unité de traitement. +la contrainte d'encombrement réduit et de puissance de calcul suffisant a conduit au choix d'une micro-ordinateur pour la partie unité de traitement.\\ 
-L'écran choisi a une taille de ____ et de résolution ____; c'est un écran qui s'interface à la Raspberry en HDMI. +L'écran choisi a une taille de 1920 par 1080 pixels; c'est un écran qui s'interface au micro-ordinateur en HDMI. 
-Il était initialement question de pions qui se poseraient dans les trous du plateau de jeu. Le choix s'est porté sur des capteurs de pression (Force Sensing Resistance). Ce choix s'est avéré peu judicieux par la suite, la détection n'est pas suffisamment robuste+ 
-Un bouton pour amorcer le jeu et le faire et évoluer et un bouton pour revenir à la configuration initiale ont été ajoutés sur les GPIO de la Raspberry.  + 
-Enfin, un bouton marche/arrêt du jeu a été intégré.+\\ 
 +Il était initialement question de pions qui se poseraient dans les trous du plateau de jeu. Le choix s'est porté sur des capteurs de pression (Force Sensing Resistance) pour la détection des pions 
 + 
 +{{media_06:fsr.jpg?400|}} 
 +\\ 
 +Un bouton pour amorcer le jeu et le faire et évoluer et un bouton pour revenir à la configuration initiale ont été ajoutés sur les GPIO du micro-ordinateur\\ 
 +Enfin, un bouton marche/arrêt du jeu a été intégré. Il est nécessaire d'effectuer un arrêt du micro-ordinateur pour éviter une corruption de la carte SD
  
 Montage des capteurs dans le plateau de jeu: Montage des capteurs dans le plateau de jeu:
  
-{{:montagecapteurs.jpeg?400|}}+{{media_09:montagecapteurs.jpeg?400|}}
  
 Écran d'accueil: Écran d'accueil:
  
-{{:installationfinale.jpeg?400|}}+{{media_07:installationfinale.jpeg?400|}}
  
 Une étape: Une étape:
  
  
-{{:installationfinale2.jpeg?400|}}+{{media_07:installationfinale2.jpeg?400|}} 
 =====Matériel===== =====Matériel=====
-Un plateau de jeu dont un carré de 4x4 trous sont évidés de façon à laisser s'enfoncer des billes+Un plateau de jeu dont un carré de 4x4 trous sont évidés de façon à laisser s'enfoncer des billes\\ 
 + 
 +16 capteurs FSR pour la détection des billes: Fournisseur Pololu, dimensions 0,2 pouces de diamètre (ce qui correspond à 0,5 cm de diamètre)\\ 
 +https://www.pololu.com/product/1695\\ 
 +Deux boutons d'interaction: démarrage et avance du jeu, retour à zéro\\
  
-16 capteurs FSR pour la détection des billes +Un bouton marche/arrêt\\ 
-Fournisseur Pololu, dimensions 0,2 pouces de diamètre (ce qui correspond à 0,5 cm de diamètre) +Une RaspberryPi pour la gestion des événements et l'affichage[[https://www.kubii.fr/les-cartes-raspberry-pi/1628-raspberry-pi-3-modele-b-1-gb-kubii-640522710850.html|kubii.fr]]\\ 
-Deux boutons d'interactiondémarrage et avance du jeu, retour à zéro+Un écran Waveshare 1920x1080 pixels: https://www.waveshare.com/15.6inch-HDMI-LCD-H-with-case.htm
  
-Un bouton marche/arrêt 
-Une RaspberryPi pour la gestion des événements et l'affichage 
-Un écran  
 =====Logiciel===== =====Logiciel=====
 Un code python pour la prise en compte des billes et l'affichage du jeu de la vie Un code python pour la prise en compte des billes et l'affichage du jeu de la vie
 OS Raspbian version Buster (10.4 (?)) OS Raspbian version Buster (10.4 (?))
-Modifications de l'OS: +Modifications de l'OS:\\ 
--prise en compte du bouton marche/arrêt par un démon +-prise en compte du bouton marche/arrêt par un démon\\ 
--lancement du programme Jeu de la vie au démarrage de l'OS +-lancement du programme Jeu de la vie au démarrage de l'OS\\ 
-Sauvegarde du système complet sur une carte SD de secours: procédure +Sauvegarde du système complet sur une carte SD de secours: procédure\\ 
--copie de la carte SD d'origine +-copie de la carte SD d'origine\\ 
--copie sur une autre carte SD +-copie sur une autre carte SD\\ 
-Liste exhaustive du matériel: +Liste exhaustive du matériel:\\ 
-Photos + 
-Codes +écran Waveshare:\\ 
-Schéma électrique pour les FSR +https://www.waveshare.com/15.6inch-HDMI-LCD-H-with-case.htm 
-Schéma d'intégration  + 
-Schéma de câblage sur la Raspberry+capteurs de pression:\\ 
 + 
 + 
 +Photos\\ 
 +Codes\\ 
 +Le code a été écrit en python et utilise la bibliothèque pygame. Il s'agit de l'adaptation du code de Trevor Appleton, que l'on peut trouver détaillé ici:\\ 
 +http://trevorappleton.blogspot.com/2013/07/python-game-of-life.html\\ 
 + 
 + 
 +======================================================= 
 + 
 +<code python> 
 + import pygame, sys 
 + from pygame.locals import * 
 + import random 
 + import RPi.GPIO as GPIO 
 + from time import sleep 
 + 
 + GPIOCasesInit = {17:(9,4), 27:(10,4), 22:(11,4), 5:(12,4), 6:(9,5), 13:(10,5), 19:(11,5), 26:(12,5), 18:(9,6), 24:(10,6),   23:(11,6), 25:(12,6), 12:(9,7), 16:(10,7), 20:(11,7), 21:(12,7)} 
 + # vérification du nombre de capteurs définis 
 + assert len(GPIOCasesInit)==16 
 + assert len(set(GPIOCasesInit.values()))==16 
 + GPIO.setmode(GPIO.BCM) 
 +# les broches sur lesquelles se trouvent les capteurs, ainsi que les broches 14 et 15, sont configurées en entrée. 
 +# Par défaut, les broches des capteurs sont à l'état bas (niveau électrique 0V) 
 +# Les broches 14 et 15 (sur lesquelles sont câblés des boutons) sont au niveau électrique haut par défaut 
 + for n in GPIOCasesInit:  
 +     GPIO.setup(n, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) 
 +     GPIO.setup(14, GPIO.IN, pull_up_down=GPIO.PUD_UP) 
 +     GPIO.setup(15, GPIO.IN, pull_up_down=GPIO.PUD_UP) 
 + # les broches 14 et 15 détecteront un front descendant  
 + GPIO.add_event_detect(14, GPIO.FALLING) 
 + GPIO.add_event_detect(15, GPIO.FALLING) 
 + #nombre d'images par seconde 
 + FPS = 10 
 + 
 + ###configure la taille de la grille 
 + WINDOWWIDTH = 1920 
 + WINDOWHEIGHT = 1080 
 + CELLSIZE = 40 
 + 
 + #les dimensions de la grille sont-elles des multiples du nombre de cellules 
 + assert WINDOWWIDTH % CELLSIZE == 0, "la largeur des cellules doit être un multiple de la taille des cellules" 
 + assert WINDOWHEIGHT % CELLSIZE == 0, "la hauteur des cellules doit être un multiple de la taille des cellules" 
 + 
 + #Determine le nombre de cellules sur l'axe vertical et l'axe horizontal\\ 
 + CELLWIDTH = WINDOWWIDTH / CELLSIZE # nombre de cellules en largeur 
 + CELLHEIGHT = WINDOWHEIGHT / CELLSIZE # nombre de cellules en hauteur 
 + 
 + # fabrication des couleurs 
 + BLACK =    (0,  0,  0) 
 + WHITE =    (255,255,255) 
 + DARKGRAY = (40, 40, 40) 
 + GREEN =    (50,255,100) 
 + RED =  (255, 0, 100) 
 + 
 + BoxExample = {1:(3,10), 2:(15,10), 3:(2,11), 4:(4,11), 5:(10,11), 6:(14,11), 7:(16,11), 8:(3,12), 9:(9,12), 10:(10,12),  11:(11,12), 12:(14,12), 13:(16,12), 14:(15,13), 15:(4,16), 16:(9,16), 17:(10,16), 18:(14,16), 19:(16,16), 20:(17,16), 21:(5,17), 22:(8,17), 23:(11,17), 24:(14,17), 25:(15,17), 26:(17,17), 27:(3,18), 28:(4,18), 29:(5,18), 30:(9,18), 31:(11,18), 32:(10,19), 33:(25,17),34:(25,18),35:(26,18)} 
 + 
 + def text_objects(text, font):  
 +     textSurface = font.render(text, True, DARKGRAY) 
 +     return textSurface, textSurface.get_rect()  
 + 
 + def afficheInit(text, x, y): 
 +     largeText = pygame.font.Font('freesansbold.ttf',75) 
 +     TextSurf, TextRect = text_objects(text, largeText) 
 +     TextRect.center = (x,y) 
 +     DISPLAYSURF.blit(TextSurf, TextRect) 
 +     pygame.display.update() 
 + 
 + #Dessin de la grille 
 + def drawGrid(): 
 +     for x in range(0, WINDOWWIDTH, CELLSIZE): # trace des lignes verticales 
 +         pygame.draw.line(DISPLAYSURF, DARKGRAY, (x,0),(x,WINDOWHEIGHT)) 
 +     for y in range (0, WINDOWHEIGHT, CELLSIZE): # trace des lignes horizontales 
 +         pygame.draw.line(DISPLAYSURF, DARKGRAY, (0,y), (WINDOWWIDTH, y)) 
 + 
 + 
 + def drawGridExample(): 
 +     for n in BoxExample: 
 +      pygame.draw.rect(DISPLAYSURF, RED, (BoxExample[n][0]*CELLSIZE, BoxExample[n][1]*CELLSIZE, CELLSIZE, CELLSIZE)) 
 + 
 + #Colore en vert les cellules vivantes et en blancs les "non-vivantes" 
 + def colourGrid(item, lifeDict): 
 +     x = item[0] 
 +     y = item[1] 
 +     y = y * CELLSIZE # transforme le tableau de dimension de la grille 
 +     x = x * CELLSIZE # transforme le tableau de dimension de la grille 
 +     if lifeDict[item] == 0: 
 +         pygame.draw.rect(DISPLAYSURF, WHITE, (x, y, CELLSIZE, CELLSIZE)) 
 +     if lifeDict[item] == 1: 
 +         pygame.draw.rect(DISPLAYSURF, GREEN, (x, y, CELLSIZE, CELLSIZE)) 
 +     return lifeDict[item]\\ 
 + 
 + #Creation un dictionnaire de l'ensemble des cellules 
 + #Toutes les cellules sont "mortes" (valeur 0) 
 + def blankGrid(): 
 +     gridDict = {} 
 +     #Creation d un dictionnaire pour toutes les cellules de la grille 
 +     for y in range (CELLHEIGHT): 
 +         for x in range (CELLWIDTH): 
 +             gridDict[x,y] = 0 #Toutes les cellules sont "mortes"  
 +     return gridDict  
 + 
 + def startingGridInit(lifeDict, GPIOCasesInit): 
 +     for ncapt in GPIOCasesInit: 
 +  if(GPIO.input(ncapt)!=0): 
 +      lifeDict[GPIOCasesInit[ncapt]] = 1 
 +     return lifeDict  
 + 
 + #Calcul du nombre de voisins vivants autour de chaque cellule 
 + def getNeighbours(item,lifeDict): 
 +     neighbours = 0 
 +     for x in range (-1,2): 
 +         for y in range (-1,2): 
 +             checkCell = (item[0]+x,item[1]+y) 
 +             if checkCell[0] < CELLWIDTH  and checkCell[0] >=0: 
 +                 if checkCell [1] < CELLHEIGHT and checkCell[1]>= 0: 
 +                     if lifeDict[checkCell] == 1: 
 +                         if x == 0 and y == 0: # la cellule centrale n'est pas prise en compte 
 +                             neighbours += 0 
 +                         else: 
 +                             neighbours += 1 
 +     return neighbours 
 +      #Calcul de la nouvelle génération par appel à la fonction 'tick' 
 + def tick(lifeDict): 
 +     newTick = {} 
 +     for item in lifeDict: 
 +         #Obtention du nombre de voisins pour cet élément 
 +         numberNeighbours = getNeighbours(item, lifeDict) 
 +         if lifeDict[item] == 1: # Pour les cellules encore vivantes 
 +             if numberNeighbours < 2: # mort par sous-population 
 +                 newTick[item] = 0 
 +             elif numberNeighbours > 3: #mort par surpopulation 
 +                 newTick[item] = 0 
 +             else:\\ 
 +                 newTick[item] = 1 # ne rien faire (cellule vivante) 
 +         elif lifeDict[item] == 0: 
 +             if numberNeighbours == 3: # naissance d'une cellule 
 +                 newTick[item] = 1 
 +             else: 
 +                 newTick[item] = 0 # ne rien faire (cellule morte) 
 +     return newTick 
 + 
 + #fonction princpale 
 + def main(): 
 +     etat=0 
 +     FLAG_PUSH = 0 
 +     pygame.init() 
 +     global DISPLAYSURF 
 +     FPSCLOCK = pygame.time.Clock() 
 +     DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT), pygame.FULLSCREEN) 
 +     pygame.display.set_caption('Game of Life'
 +     DISPLAYSURF.fill(WHITE) 
 + 
 +     lifeDict = blankGrid() #Creation un dictionnaire de cellules, initialisation a zero 
 + 
 +     #Coloration des cellules vivantes 
 +     for item in lifeDict: 
 +         colourGrid(item, lifeDict) 
 +     drawGrid() 
 +     pygame.display.update() 
 +     while True: #main game loop 
 +        for event in pygame.event.get(): 
 +          if event.type == QUIT: 
 +                  pygame.quit() 
 +                  sys.exit() 
 +                 #Indispensable: prévoir de pouvoir quitter le mode plein écran  
 +  if event.type == pygame.KEYUP: 
 +  DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT), pygame.RESIZABLE) 
 +  cs14=GPIO.input(14) 
 +  cs15=GPIO.input(15) 
 +  if ps14!=cs14:  
 +  print('ps14', ps14) 
 +  print('cs14', cs14) 
 +  if(etat==0): 
 +  afficheInit('Disposez les pions sur le plateau', WINDOWWIDTH/2, WINDOWHEIGHT/2-300) 
 +  afficheInit('et appuyez sur SUIVANT', WINDOWWIDTH/2, (WINDOWHEIGHT/2-200))  
 +  afficheInit('Quelques exemples:', WINDOWWIDTH/2, WINDOWHEIGHT/2-100) 
 +  drawGridExample() 
 +  pygame.display.update() 
 +  #passage a etat suivant 
 +  if(ps14==1 and  cs14==0): 
 +  doit_demarrer=False 
 +  nb_viv=0 
 +  for elem in GPIOCasesInit: 
 +  if(GPIO.input(elem)): 
 + doit_demarrer = True 
 + nb_viv+=1 
 + print(GPIOCasesInit[elem]) 
 + print('nb_viv', nb_viv) 
 + if doit_demarrer:  
 + etat=1 
 + lifeDict = startingGridInit(lifeDict, GPIOCasesInit) 
 + else: 
 + etat=0 
 + elif(etat==1): 
 + if(ps15==1 and cs15==0): 
 + etat=0 
 + lifeDict=blankGrid() 
 + if(ps14==1 and cs14==0): 
 +        #Calcul de l'état suivant 
 + nb_viv=0 
 + lifeDict = tick(lifeDict) 
 + print('suite'
 + for item in lifeDict: 
 + if(lifeDict[item]): 
 + nb_viv=nb_viv+1 
 + print(nb_viv) 
 + if(nb_viv==0): 
 + print('vide'
 + etat=0 
 +        #Colours the live cells, blanks the dead 
 +        for item in lifeDict: 
 +            colourGrid(item, lifeDict) 
 + drawGrid() 
 +        pygame.display.update()  
 + ps14=cs14 
 + ps15=cs15 
 + sleep(0.1)  
 +        #FPSCLOCK.tick(FPS) 
 +         
 +if __name__=='__main__': 
 +    main() 
 + 
 +</code> 
 +======================================================= 
 + 
 + 
 + 
 +Schéma électrique pour les FSR\\ 
 +Schéma d'intégration \\ 
 +Schéma de câblage sur la Raspberry\\
  
 =====Retour d'expérience===== =====Retour d'expérience=====
-FSR: 
  
-Tests effectués en mesurant la valeur de la résistance selon la pression exercée :+Il semble qu'il ne soit pas nécessaire de protéger la carte SD d'une extinction intempestive, au moins pour plusieurs cycles d'extinction/allumage. 
 + 
 +Par précaution, il faut réaliser un clone (une image disque) de la carte finale, comportant non seulement une copie du système mais aussi de tous les réglages et des codes. 
 + 
 +Ce clone servira à graver une nouvelles carte SD si celle employée ne fonctionne plus. 
 + 
 + 
 +**FSR:** 
 + 
 +__ 
 +Tests effectués en mesurant la valeur de la résistance selon la pression exercée :__
  
 De 1  je passe à des valeurs plus ou moins pertinentes selon le poids. De 1  je passe à des valeurs plus ou moins pertinentes selon le poids.
Ligne 85: Ligne 336:
  
  
- +__Modifications en vue de la version 2:__
-+
-> Le 19.07.2019 11:01, Guy Antoine a écrit : +
->> Bonjour Camille +
->> Pour la manip du jeu de la vie, les jetons font 18mm donc découpe à +
->> 19mm sur le support de 120x120 selon les plans ci-joint. +
->> Peux tu me valider cette option ? J'ai regardé pour les joncs de 20mm +
->> en plexi mais c'est très "moches" comparés à ceux en bois peints +
->> Donc on vérifieras au règlage s'il faut les lester. +
- +
- +
- +
-Modifications en vue de la version 2:+
 Il s'avère que la détection par les FSR n'est pas assez robuste. Il faut souvent un appui supplémentaire sur les billes de la part de l'utilisateur. Le mieux serait d'utiliser un contact mécanique du type contacteur ou interrupteur Il s'avère que la détection par les FSR n'est pas assez robuste. Il faut souvent un appui supplémentaire sur les billes de la part de l'utilisateur. Le mieux serait d'utiliser un contact mécanique du type contacteur ou interrupteur
  
jeu_de_la_vie.1570437515.txt.gz · Dernière modification : 2019/10/07 08:38 de Camille