Utiliser une base de donnée à la place des Listes

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Avatar de l’utilisateur
microdevweb
Messages : 1802
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Utiliser une base de donnée à la place des Listes

Message par microdevweb »

Mon idée viens de ce post http://www.purebasic.fr/french/viewtopi ... =1&t=15535 ou falsam montre que l'on peut également utiliser une base de données en mémoire.
Comme vous le savez on peut trié une liste, mais pas une map. Par contre on ne peux placer aucun filtre ni sur l'une ni sur l'autre.
Le langage Sql quant à lui permet tout cela avec une grande facilité, dans cette exemple de petit jeux Atéroide je vous montre comment utilisé un base de données en lieu et place d'une liste.

(Ps: Pour ne pas rendre un code inutilement grand je ne gérè pas les collisions)

Code : Tout sélectionner

EnableExplicit
UseSQLiteDatabase()
Enumeration 
      #MainForm
      #Ship
      #Bulet
      #Asteroid
      #Db
EndEnumeration
; La structure pour le joueur
Structure player
      X.i
      Y.i
EndStructure
Global myPlayer.player

InitSprite()
InitKeyboard()
OpenWindow(#MainForm,0,0,800,600,"Teste Db",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(#MainForm),0,0,800,600)
; Creation des sprites
;{ Le sprite pour le vaiseau un simple réctangle vert
CreateSprite(#Ship,50,80)
StartDrawing(SpriteOutput(#Ship))
Box(0,0,SpriteWidth(#Ship),SpriteHeight(#Ship),$00FF00)
StopDrawing()
; On place le joueur au centre de l'écran
myPlayer\X=400-(SpriteWidth(#Ship)/2)
myPlayer\Y=580-SpriteHeight(#Ship)
;}
;{ Le sprite pour les bales un simple cercle rouge
Define W=5
CreateSprite(#Bulet,W*2,W*2,#PB_Sprite_AlphaBlending|#PB_Sprite_PixelCollision)
StartDrawing(SpriteOutput(#Bulet))
Circle(W,W,W,$0000FF)
StopDrawing()
;}
;{ Le sprite pour les astéroides un simple cercle bleu
W=15
CreateSprite(#Asteroid,W*2,W*2,#PB_Sprite_AlphaBlending|#PB_Sprite_PixelCollision)
StartDrawing(SpriteOutput(#Asteroid))
Circle(W,W,W,$FFFF00)
StopDrawing()
;}
;{ Création de la base de données en mémoire qui va remplocer nos listes ou nos maps
If OpenDatabase(#Db,":memory:","","")=0
      MessageRequester("DataBase Error","Can not create DataBase")
      End
EndIf
; Création de la table pour nos bales
Define query$
query$="CREATE TABLE bulet ("
query$+"X INTEGER,"
query$+"Y INTEGER"
query$+")"
If DatabaseUpdate(#Db,query$)=0
      MessageRequester("DataBase Error",DatabaseError())
      End
EndIf
; Création de la table pour nos astéroides
Define query$
query$="CREATE TABLE asteroid ("
query$+"X INTEGER,"
query$+"Y INTEGER"
query$+")"
If DatabaseUpdate(#Db,query$)=0
      MessageRequester("DataBase Error",DatabaseError())
      End
EndIf
;}
;{ Ajout de plusieurs astéroides dans la table
Define N
For N=1 To 20
      query$="INSERT INTO asteroid (X,Y) VALUES ("
      query$+Str(Random(800,0))+"," ; La position X dans l'écran
      query$+Str(0-Random(800,0))+")"
      If DatabaseUpdate(#Db,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            End
      EndIf
Next
;}
Define Event
Define TireOn.b=#True
Define TimeElapset
Repeat
      Repeat
            Event=WindowEvent()
            If Event=#PB_Event_CloseWindow :End :EndIf
      Until Event=0
      ; On fait descendre les asteroide
      query$="UPDATE asteroid SET Y=Y+1"
      If DatabaseUpdate(#Db,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            End
      EndIf
      ClearScreen(RGB(0,0,0))
      ; On affiche les astéroides uniquement les visibles
      query$="SELECT * FROM asteroid WHERE Y>=-50"
      If DatabaseQuery(#Db,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            End
      EndIf
      While NextDatabaseRow(#Db)
            DisplayTransparentSprite(#Asteroid,GetDatabaseLong(#Db,0),GetDatabaseLong(#Db,1))
      Wend      
      FinishDatabaseQuery(#Db)
      ; On supprime les astéroides hors écran
      query$="DELETE FROM asteroid WHERE Y>650"
      If DatabaseUpdate(#Db,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            End
      EndIf
      ; On affiche le vaiseau
      DisplaySprite(#Ship,myPlayer\X,myPlayer\Y)
      ; Déplacement du vaiseau
      ExamineKeyboard()
      If KeyboardPushed(#PB_Key_Left) And myPlayer\X>5
            myPlayer\X-5
      EndIf
      If KeyboardPushed(#PB_Key_Right) And myPlayer\X<(795-SpriteWidth(#Ship))
            myPlayer\X+5
      EndIf
      ; Gestion du tir
      If KeyboardPushed(#PB_Key_Space) And  TireOn
            TireOn=#False ;Decative le tir
            TimeElapset=ElapsedMilliseconds(); pour lancer le chrone
            ; On ajoute la bale à la table
            query$="INSERT INTO bulet (X,Y) VALUES ("+Str(myPlayer\X+20)+","
            query$+Str(myPlayer\Y-10)+")"
            If DatabaseUpdate(#Db,query$)=0
                  MessageRequester("DataBase Error",DatabaseError())
                  End
      EndIf
      EndIf
      ; Gestion du chrone
      If Not TireOn
            If (ElapsedMilliseconds()-TimeElapset)>100
                  TireOn=#True
            EndIf
      EndIf
      ; Affichage des bales
      query$="SELECT X,Y FROM bulet ORDER BY Y DESC"
      If DatabaseQuery(#Db,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            End
      EndIf
      While NextDatabaseRow(#Db)
            DisplayTransparentSprite(#Bulet,GetDatabaseLong(#Db,0),GetDatabaseLong(#Db,1))
      Wend  
      FinishDatabaseQuery(#Db)
      ; On fait avancer les bales
       query$="UPDATE bulet SET Y=Y-6"
      If DatabaseUpdate(#Db,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            End
      EndIf
      ; On supprime les bale hors ecran
      query$="DELETE FROM bulet WHERE Y<-10"
      If DatabaseUpdate(#Db,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            End
      EndIf
      FlipBuffers()
ForEver
Voici en complément une procédure pour sauver la Db mémoire en Db fichier, noter que vous pouvez indiquer l’extension que vous souhaiter donc ceci pourrait remplacer Json dans le principe

Code : Tout sélectionner

Procedure SaveDb(IdDb,FileName$)
      Structure column
            Index$
            name$
            type$
            not_nul$
            default_value$
            primaryKey$
      EndStructure
      Structure Table
            name$
            List myColumn.column()
      EndStructure
      Protected NewList myTable.Table()
      Protected query$="Select * From sqlite_master order by type Desc, name Asc"
      Protected Db,IdFile,N
      If DatabaseQuery(IdDb,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            ProcedureReturn #False
      EndIf
      ; Sauvegarde des tables
      While NextDatabaseRow(IdDb)
            If GetDatabaseString(IdDb,0)="table"
                  AddElement(myTable())
                  With myTable()
                        \name$=GetDatabaseString(IdDb,1)
                  EndWith
            EndIf
      Wend
      FinishDatabaseQuery(IdDb)
      ForEach myTable()
            With myTable()
                  ; Relevé des infos des colonnes des tables
                  query$="PRAGMA table_info('"+\name$+"')"
                  If DatabaseQuery(IdDb,query$)=0
                        MessageRequester("DataBase Error",DatabaseError())
                        ProcedureReturn #False
                  EndIf
                  While NextDatabaseRow(IdDb)
                        AddElement(\myColumn())
                        \myColumn()\Index$=GetDatabaseString(IdDb,0)
                        \myColumn()\name$=GetDatabaseString(IdDb,1)
                        \myColumn()\type$=GetDatabaseString(IdDb,2)
                        \myColumn()\not_nul$=GetDatabaseString(IdDb,3)
                        \myColumn()\default_value$=GetDatabaseString(IdDb,4)
                        \myColumn()\primaryKey$=GetDatabaseString(IdDb,5)
                  Wend
                  FinishDatabaseQuery(IdDb)
            EndWith
      Next
      ; La nouvelle base de donnée
      IdFile=CreateFile(#PB_Any,FileName$)
      If IdFile=0
            MessageRequester("File Error","Can not create file "+FileName$)
            ProcedureReturn #False
      EndIf
      CloseFile(IdFile)
      Db=OpenDatabase(#PB_Any,FileName$,"","")
      If Db=0
            MessageRequester("DataBase Error","Can not open DataBase")
            ProcedureReturn #False
      EndIf
      ; Transfert des donnée
      ForEach myTable()
            With myTable()
                  query$="CREATE TABLE "+\name$+"("
                  N=0
                  ForEach \myColumn()
                        N+1
                        If N>1
                              query$+","
                        EndIf
                        query$+\myColumn()\name$+" "
                        query$+\myColumn()\type$+" "
                        If \myColumn()\not_nul$<>"0"
                              query$+" NOT NULL "
                        EndIf
                        If \myColumn()\primaryKey$="1"
                              query$+ "PRIMARY KEY"
                        EndIf
                  Next
                  query$+")"
                  If DatabaseUpdate(Db,query$)=0
                        MessageRequester("DataBase Error",DatabaseError())
                        ProcedureReturn #False
                  EndIf
            EndWith
      Next
      ForEach myTable()
            With myTable()
                  query$=" SELECT * FROM "+\name$
                  If DatabaseQuery(IdDb,query$)=0
                        MessageRequester("Database Error",DatabaseError())
                        ProcedureReturn #False
                  EndIf
                  While NextDatabaseRow(IdDb)
                        query$="INSERT INTO "+\name$+"("
                        N=0
                        ForEach \myColumn()
                              N+1
                              If N>1
                                    query$+","
                              EndIf
                              query$+\myColumn()\name$
                        Next
                        query$+") VALUES ("
                        For N=0 To DatabaseColumns(IdDb)-1
                              If N>0
                                    query$+","
                              EndIf
                              Select DatabaseColumnType(IdDb,N)
                                    Case #PB_Database_String
                                          query$+"'"+GetDatabaseString(IdDb,N)+"'"
                                    Case #PB_Database_Long
                                          query$+Str(GetDatabaseLong(IdDb,N))
                                    Case #PB_Database_Float
                                          query$+StrF(GetDatabaseFloat(IdDb,N))
                                    Case #PB_Database_Double
                                          query$+StrD(GetDatabaseDouble(IdDb,N))
                                    Case #PB_Database_Quad
                                          query$+Str(GetDatabaseQuad(IdDb,N))
                              EndSelect
                        Next
                        query$+")"
                        If DatabaseUpdate(Db,query$)=0
                              MessageRequester("Database Error",DatabaseError())
                              ProcedureReturn #False
                        EndIf
                  Wend  
            EndWith
      Next
      ProcedureReturn #True
EndProcedure
Dernière modification par microdevweb le ven. 16/oct./2015 11:33, modifié 4 fois.
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: Utiliser une base de donnée à la place des structure

Message par blendman »

Ton filtre, c'est pour afficher uniquement les astéroïdes qui sont à l'écran ?
Parce que gérer ça avec une BDD ça me semble quand même bien compliquée plutôt que de passer par une liste, une map ou un tableau ^^.
TU peux très bien n'afficher que les sprites qui sont à l'écran, voir les supprimer de ta liste, non ?

En tout cas, merci quand même pour ton exemple, car ça reste très intéressant, notamment pour voir comment on gère les BDD en purebasic ;.
Avatar de l’utilisateur
microdevweb
Messages : 1802
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: Utiliser une base de donnée à la place des structure

Message par microdevweb »

Bonjour Blendman, c'est à titre d'exemple évidement dans ce cas passé par une list serait tout aussi efficace. Par contre imagin e pour un programme plus complexe (de dessin par exemple) avec une simple requête Sql tu pourrais trouver toutes les formes en dessous de la souris.
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1802
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: Utiliser une base de donnée à la place des Listes

Message par microdevweb »

Petite procédure complémentaire pour sauver la Db mémoire en Db fichier (code inspiré par Falsam)

Code : Tout sélectionner

Procedure SaveDb(IdDb,FileName$)
      Structure column
            Index$
            name$
            type$
            not_nul$
            default_value$
            primaryKey$
      EndStructure
      Structure Table
            name$
            List myColumn.column()
      EndStructure
      Protected NewList myTable.Table()
      Protected query$="Select * From sqlite_master order by type Desc, name Asc"
      Protected Db,IdFile,N
      If DatabaseQuery(IdDb,query$)=0
            MessageRequester("DataBase Error",DatabaseError())
            ProcedureReturn #False
      EndIf
      ; Sauvegarde des tables
      While NextDatabaseRow(IdDb)
            If GetDatabaseString(IdDb,0)="table"
                  AddElement(myTable())
                  With myTable()
                        \name$=GetDatabaseString(IdDb,1)
                  EndWith
            EndIf
      Wend
      FinishDatabaseQuery(IdDb)
      ForEach myTable()
            With myTable()
                  ; Relevé des infos des colonnes des tables
                  query$="PRAGMA table_info('"+\name$+"')"
                  If DatabaseQuery(IdDb,query$)=0
                        MessageRequester("DataBase Error",DatabaseError())
                        ProcedureReturn #False
                  EndIf
                  While NextDatabaseRow(IdDb)
                        AddElement(\myColumn())
                        \myColumn()\Index$=GetDatabaseString(IdDb,0)
                        \myColumn()\name$=GetDatabaseString(IdDb,1)
                        \myColumn()\type$=GetDatabaseString(IdDb,2)
                        \myColumn()\not_nul$=GetDatabaseString(IdDb,3)
                        \myColumn()\default_value$=GetDatabaseString(IdDb,4)
                        \myColumn()\primaryKey$=GetDatabaseString(IdDb,5)
                  Wend
                  FinishDatabaseQuery(IdDb)
            EndWith
      Next
      ; La nouvelle base de donnée
      IdFile=CreateFile(#PB_Any,FileName$)
      If IdFile=0
            MessageRequester("File Error","Can not create file "+FileName$)
            ProcedureReturn #False
      EndIf
      CloseFile(IdFile)
      Db=OpenDatabase(#PB_Any,FileName$,"","")
      If Db=0
            MessageRequester("DataBase Error","Can not open DataBase")
            ProcedureReturn #False
      EndIf
      ; Transfert des donnée
      ForEach myTable()
            With myTable()
                  query$="CREATE TABLE "+\name$+"("
                  N=0
                  ForEach \myColumn()
                        N+1
                        If N>1
                              query$+","
                        EndIf
                        query$+\myColumn()\name$+" "
                        query$+\myColumn()\type$+" "
                        If \myColumn()\not_nul$<>"0"
                              query$+" NOT NULL "
                        EndIf
                        If \myColumn()\primaryKey$="1"
                              query$+ "PRIMARY KEY"
                        EndIf
                  Next
                  query$+")"
                  If DatabaseUpdate(Db,query$)=0
                        MessageRequester("DataBase Error",DatabaseError())
                        ProcedureReturn #False
                  EndIf
            EndWith
      Next
      ForEach myTable()
            With myTable()
                  query$=" SELECT * FROM "+\name$
                  If DatabaseQuery(IdDb,query$)=0
                        MessageRequester("Database Error",DatabaseError())
                        ProcedureReturn #False
                  EndIf
                  While NextDatabaseRow(IdDb)
                        query$="INSERT INTO "+\name$+"("
                        N=0
                        ForEach \myColumn()
                              N+1
                              If N>1
                                    query$+","
                              EndIf
                              query$+\myColumn()\name$
                        Next
                        query$+") VALUES ("
                        For N=0 To DatabaseColumns(IdDb)-1
                              If N>0
                                    query$+","
                              EndIf
                              Select DatabaseColumnType(IdDb,N)
                                    Case #PB_Database_String
                                          query$+"'"+GetDatabaseString(IdDb,N)+"'"
                                    Case #PB_Database_Long
                                          query$+Str(GetDatabaseLong(IdDb,N))
                                    Case #PB_Database_Float
                                          query$+StrF(GetDatabaseFloat(IdDb,N))
                                    Case #PB_Database_Double
                                          query$+StrD(GetDatabaseDouble(IdDb,N))
                                    Case #PB_Database_Quad
                                          query$+Str(GetDatabaseQuad(IdDb,N))
                              EndSelect
                        Next
                        query$+")"
                        If DatabaseUpdate(Db,query$)=0
                              MessageRequester("Database Error",DatabaseError())
                              ProcedureReturn #False
                        EndIf
                  Wend  
            EndWith
      Next
      ProcedureReturn #True
EndProcedure
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1802
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: Utiliser une base de donnée à la place des Listes

Message par microdevweb »

Une solution beaucoup plus simple pour sauver la base de donnée, qui n'est pas de moi mais de j'ai juste modifié une rien le code.http://www.purebasic.fr/english/viewtop ... 43#p368143

Code : Tout sélectionner

Procedure.i SQLite_BackupSqliteDatabase(sourceDB, FileName$)
      Protected result, backUp,IdFile,destinationDB
      IdFile=CreateFile(#PB_Any,FileName$)
      If IdFile=0
            MessageRequester("Acces file Error","Can not create this file "+FileName$)
            ProcedureReturn #False
      EndIf
      CloseFile(IdFile)
      destinationDB=OpenDatabase(#PB_Any,FileName$,"","")
      If destinationDB=0
            MessageRequester("DataBase Error","Can not open this DataBase "+FileName$)
            ProcedureReturn #False
      EndIf
      If IsDatabase(sourceDB) And IsDatabase(destinationDB)
            backUp = sqlite3_backup_init(DatabaseID(destinationDB), "main", DatabaseID(sourceDB), "main")
            If backUp
                  sqlite3_backup_step(backUp, -1)
                  If sqlite3_backup_finish(backUp) = 0 ;#SQLITE_OK
                        result = #True   
                  EndIf
            EndIf
      EndIf
      ProcedureReturn result
EndProcedure
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Répondre