- Les Types Abstraits de Données : listes, files, piles…
- Ada : Notions avancées et Programmation Orientée Objet
La troisième partie se termine («Enfin !» diront certains) et nous allons donc la conclure comme il se doit par un troisième TP ! Après avoir hésité entre la conception de divers programmes, tel un éditeur de carte ou un éditeur de texte, je me suis dit qu'après le difficile TP précédent et cette longue partie III, vous aviez bien mérité un TP moins décourageant et plus ludique. C'est pourquoi, après de longues réflexions, j'ai opté pour un jeu : le jeu du Serpent. Vous savez ce vieux jeu que l'on avait sur les vieux portables où un serpent devait manger sans cesse de gros pixels afin de grandir en prenant garde de ne jamais dévorer sa propre queue ou sortir de l'écran.
Comme d'habitude, je vais vous guider dans sa mise en œuvre, et comme d'habitude tout se fera en console mais… avec des couleurs ! Prêts ? Voici une image pour vous mettre en bouche :
- Cahier des charges
- Un package bien utile
- … et encore un autre !
- Quelques indications
- Une solution possible
Cahier des charges
Fonctionnalités
Comme toujours, avant de nous lancer à l'aveuglette, nous devons définir les fonctionnalités de notre programme, les limites du projet. Il n'est pas question de créer un jeu en 3D ou avec des boutons, des images, une base de données… nous allons faire plus sobre.
Notre programme devra :
- s'exécuter en console, je vous l'ai dit. Toutefois, nous ferons en sorte d'afficher quelques couleurs pour égayer tout ça et surtout rendre notre programme facilement jouable. Rassurez-vous, je vous fournirais un package pour cela;
- se jouer en temps réel, pas au tour par tour ! Si le joueur ne touche pas au clavier alors son serpent ira droit dans le mur;
- gérer et donc éviter les bogues : serpent faisant demi-tour sur lui-même, sortant de la carte ou dont le corps ne suivrait pas la tête dans certaines configurations (je ne rigole pas, vous commettrez sûrement ce genre d'erreur);
- permettre au joueur de diriger le serpent au clavier à l'aide des touches fléchées.
Organisation des types et variables
Nous ne savons pas quand le joueur perdra, notre serpent devra donc pouvoir s'allonger indéfiniment (en théorie). Il serait donc judicieux d'utiliser les TAD vus précédemment pour enregitrer les différents anneaux de notre reptile : il sera plus aisé d'agrandir notre serpent à l'aide des primitives append ou prepend qu'en déclarant des tableaux ou je ne sais quoi d'autre. De même, déplacer le serpent reviendra simplement à retirer le dernier anneau de son corps pour le placer en premier dans une nouvelle position. Vous avez le choix entre tous les TAD que vous voulez, mais les Doubly_Linked_Lists ou les Vectors sont les mieux adaptés et comme j'ai une préférence pour le type Vector, la solution que je vous fournirai utilise donc… les Doubly_Linked_Lists (cherchez la cohérence ).
Mais que contiendra notre liste ? Elle contiendra toute une suite de coordonnées : les coordonnées des différentes parties du corps du serpent. Mais ce n'est pas tout ! Le serpent ne peut se limiter à une liste de coordonnées, il faudra également que notre type T_Serpent contienne la direction de la tête du serpent.
Enfin, la taille de l'aire de jeu, la vitesse de déplacement du serpent ou les couleurs utilisées devraient être enregistrées dans des variables (ou des constantes si besoin) toutes inventoriées dans un package. En faisant cela, ces variables deviendront des variables globales, ce qui est généralement risqué mais clarifiera notre code et simplifiera toute modification des paramètres du jeu. Nous reviendrons sur les variables globales dans la prochaine partie.
Un package bien utile
Le package NT_Console
Depuis le début je vous dis que notre programme sera en couleur, donc il est temps de vous fournir le package nécessaire pour utiliser des couleurs dans la console. Il ne s'agit ni d'un package officiel, ni d'un package de mon crû (mea maxima culpa ) mais d'un vieux package mis à notre disposition par Jerry van Dijk et libre de diffusion, appelé NT_Console :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | ----------------------------------------------------------------------- -- -- File: nt_console.adb -- Description: Win95/NT console support -- Rev: 0.3 -- Date: 08-june-1999 -- Author: Jerry van Dijk -- Mail: jdijk@acm.org -- -- Copyright (c) Jerry van Dijk, 1997, 1998, 1999 -- Billie Holidaystraat 28 -- 2324 LK LEIDEN -- THE NETHERLANDS -- tel int + 31 71 531 43 65 -- -- Permission granted to use for any purpose, provided this copyright -- remains attached and unmodified. -- -- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR -- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -- ----------------------------------------------------------------------- pragma C_Pass_By_Copy (128); with Interfaces; use Interfaces; package body NT_Console is pragma Linker_Options ("-luser32"); --------------------- -- WIN32 INTERFACE -- --------------------- Beep_Error : exception; Fill_Char_Error : exception; Cursor_Get_Error : exception; Cursor_Set_Error : exception; Cursor_Pos_Error : exception; Buffer_Info_Error : exception; Set_Attribute_Error : exception; Invalid_Handle_Error : exception; Fill_Attribute_Error : exception; Cursor_Position_Error : exception; subtype DWORD is Unsigned_32; subtype HANDLE is Unsigned_32; subtype WORD is Unsigned_16; subtype SHORT is Short_Integer; subtype WINBOOL is Integer; type LPDWORD is access all DWORD; pragma Convention (C, LPDWORD); type Nibble is mod 2 ** 4; for Nibble'Size use 4; type Attribute is record Foreground : Nibble; Background : Nibble; Reserved : Unsigned_8 := 0; end record; for Attribute use record Foreground at 0 range 0 .. 3; Background at 0 range 4 .. 7; Reserved at 1 range 0 .. 7; end record; for Attribute'Size use 16; pragma Convention (C, Attribute); type COORD is record X : SHORT; Y : SHORT; end record; pragma Convention (C, COORD); type SMALL_RECT is record Left : SHORT; Top : SHORT; Right : SHORT; Bottom : SHORT; end record; pragma Convention (C, SMALL_RECT); type CONSOLE_SCREEN_BUFFER_INFO is record Size : COORD; Cursor_Pos : COORD; Attrib : Attribute; Window : SMALL_RECT; Max_Size : COORD; end record; pragma Convention (C, CONSOLE_SCREEN_BUFFER_INFO); type PCONSOLE_SCREEN_BUFFER_INFO is access all CONSOLE_SCREEN_BUFFER_INFO; pragma Convention (C, PCONSOLE_SCREEN_BUFFER_INFO); type CONSOLE_CURSOR_INFO is record Size : DWORD; Visible : WINBOOL; end record; pragma Convention (C, CONSOLE_CURSOR_INFO); type PCONSOLE_CURSOR_INFO is access all CONSOLE_CURSOR_INFO; pragma Convention (C, PCONSOLE_CURSOR_INFO); function GetCh return Integer; pragma Import (C, GetCh, "_getch"); function KbHit return Integer; pragma Import (C, KbHit, "_kbhit"); function MessageBeep (Kind : DWORD) return DWORD; pragma Import (StdCall, MessageBeep, "MessageBeep"); function GetStdHandle (Value : DWORD) return HANDLE; pragma Import (StdCall, GetStdHandle, "GetStdHandle"); function GetConsoleCursorInfo (Buffer : HANDLE; Cursor : PCONSOLE_CURSOR_INFO) return WINBOOL; pragma Import (StdCall, GetConsoleCursorInfo, "GetConsoleCursorInfo"); function SetConsoleCursorInfo (Buffer : HANDLE; Cursor : PCONSOLE_CURSOR_INFO) return WINBOOL; pragma Import (StdCall, SetConsoleCursorInfo, "SetConsoleCursorInfo"); function SetConsoleCursorPosition (Buffer : HANDLE; Pos : COORD) return DWORD; pragma Import (StdCall, SetConsoleCursorPosition, "SetConsoleCursorPosition"); function SetConsoleTextAttribute (Buffer : HANDLE; Attr : Attribute) return DWORD; pragma Import (StdCall, SetConsoleTextAttribute, "SetConsoleTextAttribute"); function GetConsoleScreenBufferInfo (Buffer : HANDLE; Info : PCONSOLE_SCREEN_BUFFER_INFO) return DWORD; pragma Import (StdCall, GetConsoleScreenBufferInfo, "GetConsoleScreenBufferInfo"); function FillConsoleOutputCharacter (Console : HANDLE; Char : Character; Length : DWORD; Start : COORD; Written : LPDWORD) return DWORD; pragma Import (Stdcall, FillConsoleOutputCharacter, "FillConsoleOutputCharacterA"); function FillConsoleOutputAttribute (Console : Handle; Attr : Attribute; Length : DWORD; Start : COORD; Written : LPDWORD) return DWORD; pragma Import (Stdcall, FillConsoleOutputAttribute, "FillConsoleOutputAttribute"); WIN32_ERROR : constant DWORD := 0; INVALID_HANDLE_VALUE : constant HANDLE := -1; STD_OUTPUT_HANDLE : constant DWORD := -11; Color_Value : constant array (Color_Type) of Nibble := (0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15); Color_Type_Value : constant array (Nibble) of Color_Type := (Black, Blue, Green, Cyan, Red, Magenta, Brown, Gray, Black, Light_Blue, Light_Green, Light_Cyan, Light_Red, Light_Magenta, Yellow, White); ----------------------- -- PACKAGE VARIABLES -- ----------------------- Output_Buffer : HANDLE; Num_Bytes : aliased DWORD; Num_Bytes_Access : LPDWORD := Num_Bytes'Access; Buffer_Info_Rec : aliased CONSOLE_SCREEN_BUFFER_INFO; Buffer_Info : PCONSOLE_SCREEN_BUFFER_INFO := Buffer_Info_Rec'Access; ------------------------- -- SUPPORTING SERVICES -- ------------------------- procedure Get_Buffer_Info is begin if GetConsoleScreenBufferInfo (Output_Buffer, Buffer_Info) = WIN32_ERROR then raise Buffer_Info_Error; end if; end Get_Buffer_Info; -------------------- -- CURSOR CONTROL -- -------------------- function Cursor_Visible return Boolean is Cursor : aliased CONSOLE_CURSOR_INFO; begin if GetConsoleCursorInfo (Output_Buffer, Cursor'Unchecked_Access) = 0 then raise Cursor_Get_Error; end if; return Cursor.Visible = 1; end Cursor_Visible; procedure Set_Cursor (Visible : in Boolean) is Cursor : aliased CONSOLE_CURSOR_INFO; begin if GetConsoleCursorInfo (Output_Buffer, Cursor'Unchecked_Access) = 0 then raise Cursor_Get_Error; end if; if Visible = True then Cursor.Visible := 1; else Cursor.Visible := 0; end if; if SetConsoleCursorInfo (Output_Buffer, Cursor'Unchecked_Access) = 0 then raise Cursor_Set_Error; end if; end Set_Cursor; function Where_X return X_Pos is begin Get_Buffer_Info; return X_Pos (Buffer_Info_Rec.Cursor_Pos.X); end Where_X; function Where_Y return Y_Pos is begin Get_Buffer_Info; return Y_Pos (Buffer_Info_Rec.Cursor_Pos.Y); end Where_Y; procedure Goto_XY (X : in X_Pos := X_Pos'First; Y : in Y_Pos := Y_Pos'First) is New_Pos : COORD := (SHORT (X), SHORT (Y)); begin Get_Buffer_Info; if New_Pos.X > Buffer_Info_Rec.Size.X then New_Pos.X := Buffer_Info_Rec.Size.X; end if; if New_Pos.Y > Buffer_Info_Rec.Size.Y then New_Pos.Y := Buffer_Info_Rec.Size.Y; end if; if SetConsoleCursorPosition (Output_Buffer, New_Pos) = WIN32_ERROR then raise Cursor_Pos_Error; end if; end Goto_XY; ------------------- -- COLOR CONTROL -- ------------------- function Get_Foreground return Color_Type is begin Get_Buffer_Info; return Color_Type_Value (Buffer_Info_Rec.Attrib.Foreground); end Get_Foreground; function Get_Background return Color_Type is begin Get_Buffer_Info; return Color_Type_Value (Buffer_Info_Rec.Attrib.Background); end Get_Background; procedure Set_Foreground (Color : in Color_Type := Gray) is Attr : Attribute; begin Get_Buffer_Info; Attr.Foreground := Color_Value (Color); Attr.Background := Buffer_Info_Rec.Attrib.Background; if SetConsoleTextAttribute (Output_Buffer, Attr) = WIN32_ERROR then raise Set_Attribute_Error; end if; end Set_Foreground; procedure Set_Background (Color : in Color_Type := Black) is Attr : Attribute; begin Get_Buffer_Info; Attr.Foreground := Buffer_Info_Rec.Attrib.Foreground; Attr.Background := Color_Value (Color); if SetConsoleTextAttribute (Output_Buffer, Attr) = WIN32_ERROR then raise Set_Attribute_Error; end if; end Set_Background; -------------------- -- SCREEN CONTROL -- -------------------- procedure Clear_Screen (Color : in Color_Type := Black) is Length : DWORD; Attr : Attribute; Home : COORD := (0, 0); begin Get_Buffer_Info; Length := DWORD (Buffer_Info_Rec.Size.X) * DWORD (Buffer_Info_Rec.Size.Y); Attr.Background := Color_Value (Color); Attr.Foreground := Buffer_Info_Rec.Attrib.Foreground; if SetConsoleTextAttribute (Output_Buffer, Attr) = WIN32_ERROR then raise Set_Attribute_Error; end if; if FillConsoleOutputAttribute (Output_Buffer, Attr, Length, Home, Num_Bytes_Access) = WIN32_ERROR then raise Fill_Attribute_Error; end if; if FillConsoleOutputCharacter (Output_Buffer, ' ', Length, Home, Num_Bytes_Access) = WIN32_ERROR then raise Fill_Char_Error; end if; if SetConsoleCursorPosition (Output_Buffer, Home) = WIN32_ERROR then raise Cursor_Position_Error; end if; end Clear_Screen; ------------------- -- SOUND CONTROL -- ------------------- procedure Bleep is begin if MessageBeep (16#FFFFFFFF#) = WIN32_ERROR then raise Beep_Error; end if; end Bleep; ------------------- -- INPUT CONTROL -- ------------------- function Get_Key return Character is Temp : Integer; begin Temp := GetCh; if Temp = 16#00E0# then Temp := 0; end if; return Character'Val (Temp); end Get_Key; function Key_Available return Boolean is begin if KbHit = 0 then return False; else return True; end if; end Key_Available; begin -------------------------- -- WIN32 INITIALIZATION -- -------------------------- Output_Buffer := GetStdHandle (STD_OUTPUT_HANDLE); if Output_Buffer = INVALID_HANDLE_VALUE then raise Invalid_Handle_Error; end if; end NT_Console; |
NT_Console.adb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | ----------------------------------------------------------------------- -- -- File: nt_console.ads -- Description: Win95/NT console support -- Rev: 0.2 -- Date: 08-june-1999 -- Author: Jerry van Dijk -- Mail: jdijk@acm.org -- -- Copyright (c) Jerry van Dijk, 1997, 1998, 1999 -- Billie Holidaystraat 28 -- 2324 LK LEIDEN -- THE NETHERLANDS -- tel int + 31 71 531 43 65 -- -- Permission granted to use for any purpose, provided this copyright -- remains attached and unmodified. -- -- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR -- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -- ----------------------------------------------------------------------- package NT_Console is ---------------------- -- TYPE DEFINITIONS -- ---------------------- subtype X_Pos is Natural range 0 .. 79; subtype Y_Pos is Natural range 0 .. 24; type Color_Type is (Black, Blue, Green, Cyan, Red, Magenta, Brown, Gray, Light_Blue, Light_Green, Light_Cyan, Light_Red, Light_Magenta, Yellow, White); ---------------------- -- EXTENDED PC KEYS -- ---------------------- Key_Alt_Escape : constant Character := Character'Val (16#01#); Key_Control_At : constant Character := Character'Val (16#03#); Key_Alt_Backspace : constant Character := Character'Val (16#0E#); Key_BackTab : constant Character := Character'Val (16#0F#); Key_Alt_Q : constant Character := Character'Val (16#10#); Key_Alt_W : constant Character := Character'Val (16#11#); Key_Alt_E : constant Character := Character'Val (16#12#); Key_Alt_R : constant Character := Character'Val (16#13#); Key_Alt_T : constant Character := Character'Val (16#14#); Key_Alt_Y : constant Character := Character'Val (16#15#); Key_Alt_U : constant Character := Character'Val (16#16#); Key_Alt_I : constant Character := Character'Val (16#17#); Key_Alt_O : constant Character := Character'Val (16#18#); Key_Alt_P : constant Character := Character'Val (16#19#); Key_Alt_LBracket : constant Character := Character'Val (16#1A#); Key_Alt_RBracket : constant Character := Character'Val (16#1B#); Key_Alt_Return : constant Character := Character'Val (16#1C#); Key_Alt_A : constant Character := Character'Val (16#1E#); Key_Alt_S : constant Character := Character'Val (16#1F#); Key_Alt_D : constant Character := Character'Val (16#20#); Key_Alt_F : constant Character := Character'Val (16#21#); Key_Alt_G : constant Character := Character'Val (16#22#); Key_Alt_H : constant Character := Character'Val (16#23#); Key_Alt_J : constant Character := Character'Val (16#24#); Key_Alt_K : constant Character := Character'Val (16#25#); Key_Alt_L : constant Character := Character'Val (16#26#); Key_Alt_Semicolon : constant Character := Character'Val (16#27#); Key_Alt_Quote : constant Character := Character'Val (16#28#); Key_Alt_Backquote : constant Character := Character'Val (16#29#); Key_Alt_Backslash : constant Character := Character'Val (16#2B#); Key_Alt_Z : constant Character := Character'Val (16#2C#); Key_Alt_X : constant Character := Character'Val (16#2D#); Key_Alt_C : constant Character := Character'Val (16#2E#); Key_Alt_V : constant Character := Character'Val (16#2F#); Key_Alt_B : constant Character := Character'Val (16#30#); Key_Alt_N : constant Character := Character'Val (16#31#); Key_Alt_M : constant Character := Character'Val (16#32#); Key_Alt_Comma : constant Character := Character'Val (16#33#); Key_Alt_Period : constant Character := Character'Val (16#34#); Key_Alt_Slash : constant Character := Character'Val (16#35#); Key_Alt_KPStar : constant Character := Character'Val (16#37#); Key_F1 : constant Character := Character'Val (16#3B#); Key_F2 : constant Character := Character'Val (16#3C#); Key_F3 : constant Character := Character'Val (16#3D#); Key_F4 : constant Character := Character'Val (16#3E#); Key_F5 : constant Character := Character'Val (16#3F#); Key_F6 : constant Character := Character'Val (16#40#); Key_F7 : constant Character := Character'Val (16#41#); Key_F8 : constant Character := Character'Val (16#42#); Key_F9 : constant Character := Character'Val (16#43#); Key_F10 : constant Character := Character'Val (16#44#); Key_Home : constant Character := Character'Val (16#47#); Key_Up : constant Character := Character'Val (16#48#); Key_PageUp : constant Character := Character'Val (16#49#); Key_Alt_KPMinus : constant Character := Character'Val (16#4A#); Key_Left : constant Character := Character'Val (16#4B#); Key_Center : constant Character := Character'Val (16#4C#); Key_Right : constant Character := Character'Val (16#4D#); Key_Alt_KPPlus : constant Character := Character'Val (16#4E#); Key_End : constant Character := Character'Val (16#4F#); Key_Down : constant Character := Character'Val (16#50#); Key_PageDown : constant Character := Character'Val (16#51#); Key_Insert : constant Character := Character'Val (16#52#); Key_Delete : constant Character := Character'Val (16#53#); Key_Shift_F1 : constant Character := Character'Val (16#54#); Key_Shift_F2 : constant Character := Character'Val (16#55#); Key_Shift_F3 : constant Character := Character'Val (16#56#); Key_Shift_F4 : constant Character := Character'Val (16#57#); Key_Shift_F5 : constant Character := Character'Val (16#58#); Key_Shift_F6 : constant Character := Character'Val (16#59#); Key_Shift_F7 : constant Character := Character'Val (16#5A#); Key_Shift_F8 : constant Character := Character'Val (16#5B#); Key_Shift_F9 : constant Character := Character'Val (16#5C#); Key_Shift_F10 : constant Character := Character'Val (16#5D#); Key_Control_F1 : constant Character := Character'Val (16#5E#); Key_Control_F2 : constant Character := Character'Val (16#5F#); Key_Control_F3 : constant Character := Character'Val (16#60#); Key_Control_F4 : constant Character := Character'Val (16#61#); Key_Control_F5 : constant Character := Character'Val (16#62#); Key_Control_F6 : constant Character := Character'Val (16#63#); Key_Control_F7 : constant Character := Character'Val (16#64#); Key_Control_F8 : constant Character := Character'Val (16#65#); Key_Control_F9 : constant Character := Character'Val (16#66#); Key_Control_F10 : constant Character := Character'Val (16#67#); Key_Alt_F1 : constant Character := Character'Val (16#68#); Key_Alt_F2 : constant Character := Character'Val (16#69#); Key_Alt_F3 : constant Character := Character'Val (16#6A#); Key_Alt_F4 : constant Character := Character'Val (16#6B#); Key_Alt_F5 : constant Character := Character'Val (16#6C#); Key_Alt_F6 : constant Character := Character'Val (16#6D#); Key_Alt_F7 : constant Character := Character'Val (16#6E#); Key_Alt_F8 : constant Character := Character'Val (16#6F#); Key_Alt_F9 : constant Character := Character'Val (16#70#); Key_Alt_F10 : constant Character := Character'Val (16#71#); Key_Control_Left : constant Character := Character'Val (16#73#); Key_Control_Right : constant Character := Character'Val (16#74#); Key_Control_End : constant Character := Character'Val (16#75#); Key_Control_PageDown : constant Character := Character'Val (16#76#); Key_Control_Home : constant Character := Character'Val (16#77#); Key_Alt_1 : constant Character := Character'Val (16#78#); Key_Alt_2 : constant Character := Character'Val (16#79#); Key_Alt_3 : constant Character := Character'Val (16#7A#); Key_Alt_4 : constant Character := Character'Val (16#7B#); Key_Alt_5 : constant Character := Character'Val (16#7C#); Key_Alt_6 : constant Character := Character'Val (16#7D#); Key_Alt_7 : constant Character := Character'Val (16#7E#); Key_Alt_8 : constant Character := Character'Val (16#7F#); Key_Alt_9 : constant Character := Character'Val (16#80#); Key_Alt_0 : constant Character := Character'Val (16#81#); Key_Alt_Dash : constant Character := Character'Val (16#82#); Key_Alt_Equals : constant Character := Character'Val (16#83#); Key_Control_PageUp : constant Character := Character'Val (16#84#); Key_F11 : constant Character := Character'Val (16#85#); Key_F12 : constant Character := Character'Val (16#86#); Key_Shift_F11 : constant Character := Character'Val (16#87#); Key_Shift_F12 : constant Character := Character'Val (16#88#); Key_Control_F11 : constant Character := Character'Val (16#89#); Key_Control_F12 : constant Character := Character'Val (16#8A#); Key_Alt_F11 : constant Character := Character'Val (16#8B#); Key_Alt_F12 : constant Character := Character'Val (16#8C#); Key_Control_Up : constant Character := Character'Val (16#8D#); Key_Control_KPDash : constant Character := Character'Val (16#8E#); Key_Control_Center : constant Character := Character'Val (16#8F#); Key_Control_KPPlus : constant Character := Character'Val (16#90#); Key_Control_Down : constant Character := Character'Val (16#91#); Key_Control_Insert : constant Character := Character'Val (16#92#); Key_Control_Delete : constant Character := Character'Val (16#93#); Key_Control_KPSlash : constant Character := Character'Val (16#95#); Key_Control_KPStar : constant Character := Character'Val (16#96#); Key_Alt_EHome : constant Character := Character'Val (16#97#); Key_Alt_EUp : constant Character := Character'Val (16#98#); Key_Alt_EPageUp : constant Character := Character'Val (16#99#); Key_Alt_ELeft : constant Character := Character'Val (16#9B#); Key_Alt_ERight : constant Character := Character'Val (16#9D#); Key_Alt_EEnd : constant Character := Character'Val (16#9F#); Key_Alt_EDown : constant Character := Character'Val (16#A0#); Key_Alt_EPageDown : constant Character := Character'Val (16#A1#); Key_Alt_EInsert : constant Character := Character'Val (16#A2#); Key_Alt_EDelete : constant Character := Character'Val (16#A3#); Key_Alt_KPSlash : constant Character := Character'Val (16#A4#); Key_Alt_Tab : constant Character := Character'Val (16#A5#); Key_Alt_Enter : constant Character := Character'Val (16#A6#); -------------------- -- CURSOR CONTROL -- -------------------- function Cursor_Visible return Boolean; procedure Set_Cursor (Visible : in Boolean); function Where_X return X_Pos; function Where_Y return Y_Pos; procedure Goto_XY (X : in X_Pos := X_Pos'First; Y : in Y_Pos := Y_Pos'First); ------------------- -- COLOR CONTROL -- ------------------- function Get_Foreground return Color_Type; function Get_Background return Color_Type; procedure Set_Foreground (Color : in Color_Type := Gray); procedure Set_Background (Color : in Color_Type := Black); -------------------- -- SCREEN CONTROL -- -------------------- procedure Clear_Screen (Color : in Color_Type := Black); ------------------- -- SOUND CONTROL -- ------------------- procedure Bleep; ------------------- -- INPUT CONTROL -- ------------------- function Get_Key return Character; function Key_Available return Boolean; end NT_Console; |
NT_Console.ads
Vous savez désormais comment fonctionnent les packages : copiez chacun de ces textes dans un fichier séparé et enregistrez-les sous les noms NT_Console.adb et NT_Console.ads dans le répertoire de votre projet et n'oubliez pas d'écrire la ligne ci-dessous en en-tête de votre projet :
1 | With NT_Console ; Use NT_Console ; |
Le contenu en détail
Voyons maintenant le contenu de ce package. Pour cela, ouvrez le fichier NT_Console.ads (le code source ne nous intéresse pas, nous n'allons observer que les spécifications). Pour les plus craintifs, suivez le guide. Pour ceux qui peuvent s'en sortir seuls (et c'est faisable) lisez ce fichier par vous-mêmes.
C'est l'histoire de trois types…
Ce package est très bien ficelé, il commence par présenter le package : nom, nom de l'auteur, version, date de création, droits… Je vous conseille de vous en inspirer pour plus de lisibilité dans vos packages. Mais venons-en à ce qui nous intéresse. NT_Console commence par définir trois types et seulement trois :
- X_Pos : c'est un Natural entre 0 et 79. Il correspond à la position de votre curseur sur une ligne de votre console : le premier emplacement est le numéro 0, le dernier est le numéro 79. Autrement dit, vous pouvez afficher 80 caractères par ligne.
- Y_Pos : c'est un Natural entre 0 et 24. Il correspond au numéro de la ligne à laquelle se situe votre curseur. La première ligne est la numéro 0, la dernière la numéro 24 d'où un total de 25 lignes affichables dans la console. A elles deux, des variables de type X_Pos et Y_Pos vous indiquent où se situe votre curseur à l'écran. Attention, encore une fois, le premier emplacement est le numéro
(0,0)
- Color_Type : c'est un type énuméré comptant 15 noms de couleur. C'est ce type qui va nous servir à définir la couleur du texte ou de l'arrière plan. Pour ceux qui auraient du mal avec l'anglais, c'est comme si vous aviez ceci :
1 2 3 4 | type Color_Type is (Noir, Bleu, Vert, Cyan, Rouge, Magenta, Marron, Gris, Bleu_Clair, Vert_Clair, Cyan_clair, Rouge_Clair, Magenta_Clair, Jaune, Blanc); |
Puis, vient une longue (très longue) liste de constantes correspondant aux valeurs de certaines touches ou combinaisons de touches du clavier.
Et pour quelques fonctions de plus
Nous en venons donc à l'essentiel : les procédures et fonctions. Elles sont très bien rangées en cinq catégories : contrôle du curseur, contrôle de la couleur, contrôle de l'écran, contrôle du son et contrôle des entrées (entrées-clavier bien sûr).
Contrôle du curseur
Pour savoir si le curseur est visible ou non, utilisez la fonction Cursor_Visible()
. Pour définir si le curseur sera visible ou non utilisez la procédure Set_Cursor()
. Pour connaître la position du curseur, c'est à dire connaître ses coordonnées X_Pos et Y_Pos, vous pourrez utiliser les fonctions Where_X et Where_Y (Where = où). Enfin, pour modifier cette position, vous utiliserez la procédure Goto_XY()
qui prend en paramètre une variable de type X_Pos et une de type Y_Pos.
Contrôle de la couleur
Il y a deux éléments dont nous pouvons modifier la couleur à l'affichage : la couleur du texte (l'avant-plan ou premier plan) et la couleur du fond, du surlignage (l'arrière-plan). C'est à cela que correspondent Foreground (l'avant-plan) et Background (arrière-plan).
Comme toujours, deux actions sont possibles : lire et écrire. Si vous souhaitez connaître la couleur de l'arrière plan, vous devez la lire et vous utiliserez donc la fonction get_Background
qui vous retournera la couleur de fond. Si vous souhaitez modifier cette couleur, alors vous utiliserez la procédure Set_Background(une_couleur)
. Même chose pour l'avant-plan bien entendu.
Retenez donc ceci :
- Foreground = Avant-plan / Background = Arrière-plan
- Get = Saisir / Set = Définir
Contrôle de l'écran
Cette section, comme la suivante, ne comporte qu'une seule procédure : Clear_Screen()
. Cette procédure prend en paramètre une couleur (une variable de type Color_Type) et, comme son nom l'indique, nettoie l'écran. Plus rien n'est affiché et en plus vous pouvez changer en même temps la couleur de fond de la console. Attention, la couleur de fond de la console est différente de la couleur d'arrière-plan de votre texte !
Contrôle du son
Une seule procédure sans grand intérêt : Bleep
. Cette procédure se contente d'émettre un bip, comme si Windows avait rencontré une erreur.
Contrôle des entées clavier
Lorsque l'utilisateur appuie sur les touches du clavier (même sans que votre programme ne l'y ai invité), celui-ci transmet une information à votre ordinateur qui la stocke en mémoire (on parle de mémoire tampon ou de Buffer, souvenez-vous, nous en avions parlé quand nous avions vu l'instruction Skip_line). Ainsi, la mémoire tampon peut contenir toute une série de caractères avant même que le programme n'en ait besoin ou bien être vide alors que le programme attend une réponse. La fonction key_available vous permet de savoir si une touche a été stockée en mémoire tampon. Les fonctions et procédures get, get_line, get_immediate… se contentent ainsi de piocher dans la mémoire tampon, au besoin en attendant qu'elle se remplisse.
La fonction get_key quant à elle agit un peu à la manière de la procédure get_immediate : elle pioche immédiatement dans la mémoire tampon sans attendre que l'utilisateur valide la saisie avec Entrée. Quel intérêt ? Eh bien il y a une petite différence, notamment dans la gestion des touches «spéciales». Par «touches spéciales», j'entends les touches flèches ou F10 par exemple. Essayez ce code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | with nt_console ; use nt_console ; with ada.text_io ; use ada.Text_IO ; with ada.Integer_Text_IO ; use ada.Integer_Text_IO ; procedure test is c : character ; begin loop c := get_key ; put("valeur correspondante :") ; put(character'pos(c)) ; new_line ; end loop ; end test01 ; |
Vous vous rendrez compte que toute touche spéciale envoie en fait deux caractères : le caractère n°0 suivi d'un second. Ainsi, la flèche gauche envoie le caractère n°0 suivi du caractère n°75. À l'aide de ce petit programme, vous pourrez récupérer les numéros des touches fléchées qui serviront au joueur à diriger le serpent.
La fonction key_available quant à elle indique si le buffer est vide ou pas. Si elle renvoie TRUE
, c'est qu'il y a au moins un caractère en mémoire tampon et que l'on peut le saisir. Chose importante, elle n'attend pas que l'utilisateur appuie sur un touche ! Ainsi, à l'aide de cette fonction, nous pourrons faire en sorte que le serpent continue à avancer sans attendre que l'utilisateur appuie sur une touche ! Nous n'utiliserons get_key que dans le cas où key_available aura préalablement renvoyé TRUE
.
… et encore un autre !
Pour mesurer le temps et définir ainsi la vitesse de jeu, nous aurons besoin d'un second package (officiel celui-là) : Ada.Calendar !
Le package Ada.calendar est fait pour nous permettre de manipuler l'horloge. Si vous cherchez le fichier correspondant, il porte le nom a-calend.ads. Deux types essentiels le composent : le type Duration et le type Time (même si Time est une copie de Duration). Duration mesure les durées, Time correspond aux dates. Ce sont tous deux des nombres décimaux, puisque le temps est mesuré en secondes. Attention toutefois aux problèmes de typage ! Nous allons définir deux variables : temps et duree.
1 2 | temps : time ; duree : Duration ; |
Pour saisir la date actuelle (jour, mois, année, seconde), il faut utiliser la fonction clock :
1 | temps := clock ; |
Il est ensuite possible d'effectuer des opérations, par exemple :
1 | duree := clock - temps ; |
En soustrayant le temps enregistré dans la variable temps au temps actuel fourni par la fonction clock, on obtient une durée. Il est également possible de soustraire (ou d'ajouter) une durée à un temps, ce qui donne alors un temps. Il est également possible de comparer des variables de type Time ou Duration. Enfin, pour afficher une variable de type Time ou Duration, pensez à la convertir préalablement en Float. Pour en savoir plus sur le package Ada.Calendar, n'hésitez pas à éplucher les fichiers a-calend.adb et a-calend.ads, ils ne sont pas très longs.
Quelques indications
Jouer en temps réel
Comment je fais pour jouer en temps réel ? Mon serpent ne peut pas avancer tout seul et en même temps attendre que je lui indique la direction à prendre !
Nous allons devoir combiner les deux packages dont je viens de vous parler pour parvenir à ce petit exploit. Le serpent avance case par case, chaque avancée prend un certain temps (pour ma part, j'ai pris une durée de 0.2 secondes) durant lequel le joueur peut appuyer sur les touches de son clavier pour modifier la direction du serpent. Une fois ce temps écoulé, le serpent avance d'une case. Cela nous amène à l'algorithme suivant :
1 2 3 4 | TANT QUE duree<0.2 REPETER | attendre_une_réaction_du_joueur ; FIN REPETER Avancer_le_serpent ; |
Le soucis, c'est que si le joueur n'appuie jamais sur le clavier, le programme reste bloqué sur l'instruction attendre_une_réaction_du_joueur et on ne sort jamais de la boucle, non?
C'est là qu'intervient le package NT_Console ! Nous ne devons pas attendre que le joueur ait saisi une touche mais simplement nous demander s'il a appuyé sur une touche. Si oui, on saisit effectivement cette touche, sinon on continue à parcourir notre boucle. Cela nous amène à l'algorithme suivant :
1 2 3 | if Key_available then c:=get_key ; end if ; |
Ainsi, le programme ne reste pas bloqué sur get_key indéfiniment. Cependant, nous attendons que le joueur appuie sur une touche fléchée, pas sur une lettre ! Comme je vous l'ai dit, les touches spéciales envoient deux caractères consécutivement : le numéro 0 puis le véritable caractère. Donc nous devons reprendre le code précédent ainsi :
1 2 3 4 5 6 | if Key_available then if character'pos(get_key)=0 then c:=get_key ; traitement ; end if ; end if ; |
Comment afficher un serpent et une zone de jeu en couleur ?
Pour cela, regardons la capture d'écran que je vous ai fournie, elle n'est pas parole d'évangile mais elle va nous guider :
Que remarque-t-on ? Tout d'abord, le fond de la console est gris, contrairement à l'aire de jeu qui elle est blanche et au serpent qui est dessiné en bleu par-dessus l'aire de jeu. Nous avons donc trois niveau de couleur :
- La couleur de fond de la console : elle sera obtenue lorsque vous utiliser la procédure
clear_screen()
. - La couleur de la zone de jeu : elle est obtenue en fixant la couleur d'arrière-plan et en affichant de simples espaces. Attention à ne pas afficher de caractères avec un arrière-plan blanc en dehors de cette zone. Notamment si vous affichez du texte comme le score ou un message de défaite.
- La couleur des items affichés sur la zone de jeu : ces items comme les morceaux du serpent ou l'objet qu'il doit manger, doivent avoir un arrière-plan blanc et un avant-plan bleu (ou n'importe quelle autre couleur de votre choix).
Pensez également que lorsque votre serpent avancera d'une case, il ne suffira pas d'afficher le nouveau serpent, il faudra également penser à effacer l'ancien de l'écran !
Mais d'où tu sors tes ronds, tes rectangles et tes triangles ?
C'est simple. Il suffit de réaliser un petit programme qui affiche tous les caractères du numéro 0 au numéro 255 (à l'aide des attributs 'pos
et 'val
). On découvre ainsi plein de caractères bizarroïdes comme le trèfle, le cœur, le pique et le carreau ou des flèches, des carrés… Pour les rectangles du corps du serpent, j'ai utilisé character'val(219)
et character'val(178)
, pour les triangles j'ai utilisé character'val(16)
, character'val(17)
, character'val(30)
ou character'val(31)
et quant à l'espèce de soleil, il s'agit de character'val(15)
. Mais ne vous contentez pas de reprendre mot à mot mes propositions, faites des essais et trouvez les symboles qui vous paraissent les plus appropriés.
Par où commencer ?
Nous en sommes au troisième TP, il serait bon que vous commenciez à établir vous-même votre approche du problème. Je ne vais donc pas détailler énormément cette partie. Voici comment j'aborderais ce TP (en entrecoupant chaque étape de divers tests pour trouver d'éventuels bogues) :
- Création des types nécessaires pour faire un serpent.
- Réalisation des procédures d'affichage de l'aire de jeu et du serpent.
- Réalisation des procédures de déplacement et d'agrandissement du serpent + actualisation éventuelle des procédures d'affichage.
- Réalisation des procédures permettant au Joueur de piloter le Serpent au clavier.
- Mise en place des règles : interdiction de sortir de l'espace de jeu, de mordre le corps du serpent, d'effectuer des demi-tours.
- Réalisation des procédures générant l'item à avaler (appelé Anneau dans mon code) et actualisation des règles : «si le serpent mange l'anneau alors il grandit», «un anneau ne peut apparaître directement sur le serpent».
- Éventuels débogages et ajouts de fonctionnalités.
Voilà pour le plan de bataille. Il est succinct mais devrait vous fournir des objectifs partiels facilement atteignables. Je me répète mais n'hésitez pas à effectuer des tests réguliers et approfondis pour être sûr de ne pas avoir créé de bogues. Par exemple, ce n'est pas parce que votre serpent avance correctement vers le haut, qu'il avancera correctement vers la droite ou après avoir effectué un ou deux virages. Et en cas de difficultés persistantes, n'hésitez pas à poser des questions.
Une solution possible
Le fichier principal :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | with nt_console ; use nt_console ; with Snake_Variables ; use Snake_Variables ; with Snake_Programs ; use Snake_Programs ; with Snake_Screen ; use Snake_Screen ; procedure Snake is Serpent : T_Serpent ; begin Set_Cursor(false) ; print_ecran(snake) ; print_ecran(ready) ; print_ecran(start) ; Clear_screen(Couleur_Ecran) ; print_plateau ; Init_Serpent(Serpent) ; Print_Serpent(Serpent) ; Game(Serpent) ; end Snake ; |
Le package contenant quelques types et surtout les variables nécessaires au programme :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | with nt_console ; use nt_console ; package Snake_Variables is type T_Coord is record x,y : integer := 0 ; end record ; Longueur : Natural := 30 ; Hauteur : Natural := 15 ; subtype Intervalle_Alea is natural range 1..Longueur*Hauteur ; HDecalage : Natural := (X_Pos'last-X_Pos'first+1-Longueur)/2 ; VDecalage : Natural := (Y_Pos'last-Y_Pos'first+1-Hauteur)/2 ; Couleur_Ecran : Color_Type := gray ; Couleur_Fond : Color_Type := white ; Couleur_Texte : Color_Type := light_blue ; Couleur_Anneau: Color_Type := red ; Duree : Duration := 0.2 ; Score : Natural := 0 ; touche : Character ; end Snake_Variables ; |
Le package contenant la plupart des programmes servant au jeu :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | with nt_console ; use nt_console ; with ada.text_io ; use ada.Text_IO ; with ada.Integer_Text_IO ; use ada.Integer_Text_IO ; with ada.Calendar ; use ada.Calendar ; package body Snake_Programs is --------------------------------- -- AFFICHAGE PLATEAU -- --------------------------------- procedure print_plateau is begin print_score ; new_line(Count(VDecalage)) ; for j in 1..Hauteur loop set_background(Couleur_Ecran) ; for i in 1..HDecalage loop put(' ') ; end loop ; set_background(Couleur_Fond) ; for i in 1..longueur loop put(' ') ; end loop ; new_line ; end loop ; end print_plateau ; procedure print_score is begin set_foreground(green) ; set_background(Couleur_Ecran) ; goto_XY(0,0) ; put("SCORE = ") ; put(Score,4) ; set_foreground(Couleur_Texte) ; set_background(Couleur_Fond) ; end print_score ; -------------------------------- -- CREATION SERPENT -- -------------------------------- procedure Init_Serpent(Serpent : in out T_Serpent) is begin Append(Serpent.corps,(15,7)) ; --placement des anneaux du serpent Append(Serpent.corps,(15,8)) ; Append(Serpent.corps,(15,9)) ; Append(Serpent.corps,(14,9)) ; Append(Serpent.corps,(13,9)) ; Serpent.curseur := First(Serpent.corps) ; --placement du curseur sur la tête du serpent Serpent.direction := (0,-1) ; --direction vers le haut end Init_Serpent ; procedure move(Serpent : in out T_Serpent) is coord: T_coord ; begin efface_queue(Serpent) ; --on efface la queue coord.x := Element(Serpent.curseur ).x + Serpent.direction.x ; coord.y := Element(Serpent.curseur ).y + Serpent.direction.y ; Prepend(Serpent.corps,coord) ; --on ajoute une nouvelle tête Serpent.curseur := Last(Serpent.corps) ; Delete(serpent.corps,Serpent.curseur) ; --on supprime la queue Serpent.curseur := First(Serpent.corps) ; print_serpent(serpent) ; --on affiche le nouveau corps end move ; procedure grow(Serpent : in out T_Serpent) is coord: T_coord ; begin coord.x := First_Element(Serpent.corps).x + Serpent.direction.x ; coord.y := First_Element(Serpent.corps).y + Serpent.direction.y ; Prepend(Serpent.corps,coord) ; --on ajoute une nouvelle tête Serpent.curseur := First(Serpent.corps) ; print_serpent(serpent) ; --on affiche le nouveau corps end grow ; function est_sorti(Serpent : T_Serpent) return boolean is tete : T_Coord ; begin tete := First_Element(Serpent.corps) ; if tete.x < 1 or tete.x > Longueur or tete.y < 1 or tete.y > hauteur then return true ; else return false ; end if ; end est_sorti ; function est_mordu(Serpent : T_Serpent) return boolean is tete : T_Coord ; Serpent2 : T_Serpent := Serpent ; begin tete := First_Element(Serpent2.corps) ; Delete_first(Serpent2.corps) ; if Find(Serpent2.corps,tete) /= No_Element --Is_In(tete,Serpent2.corps) then return true ; else return false ; end if ; end est_mordu ; function a_mange(Serpent : T_Serpent ; Anneau : T_Coord) return boolean is tete : T_Coord ; begin tete := First_Element(Serpent.corps) ; if tete = Anneau then return true ; else return false ; end if ; end a_mange ; --------------------------------- -- AFFICHAGE SERPENT -- --------------------------------- procedure print_tete(Serpent : in T_Serpent) is begin if serpent.direction.x < 0 then put(character'val(17)) ; --regard vers la gauche elsif serpent.direction.x > 0 then put(character'val(16)) ; --regard vers la droite elsif serpent.direction.y <0 then put(character'val(30)) ; --regard vers le haut else put(character'val(31)) ; --regard vers le bas end if ; end print_tete ; procedure print_corps(nb : natural) is begin if nb mod 2 =0 then put(character'val(219)) ; else put(character'val(178)) ; end if ; end print_corps ; procedure print_serpent(Serpent : in out T_Serpent) is begin Set_Foreground(Couleur_Texte) ; for i in 1..length(Serpent.corps) loop Goto_XY(Element(serpent.curseur).x + HDecalage-1, Element(serpent.curseur).y + VDecalage-1) ; if i = 1 then print_tete(serpent) ; else print_corps(integer(i)) ; end if ; Next(Serpent.curseur) ; end loop ; Serpent.curseur := First(Serpent.corps) ; end print_serpent ; procedure efface_queue(Serpent : in out T_Serpent) is begin Serpent.curseur := Last(Serpent.corps) ; Goto_XY(Element(serpent.curseur).x + HDecalage-1, Element(serpent.curseur).y + VDecalage-1) ; put(' ') ; Serpent.curseur := First(Serpent.corps) ; end efface_queue ; --------------------------------- -- GESTION DES ANNEAUX -- --------------------------------- function generer(germe : generator ; Serpent : T_Serpent) return T_Coord IS temp1,temp2 : Natural ; anneau : T_Coord ; BEGIN loop temp1 := random(germe) ; temp2 := random(germe) ; Anneau := (temp1 mod longueur + 1, temp2 mod hauteur +1) ; if Find(Serpent.corps,Anneau) = No_Element then return Anneau ; end if ; end loop ; end generer ; procedure print_anneau(anneau : T_Coord) is begin set_foreground(Couleur_Anneau) ; Goto_XY(anneau.x+Hdecalage-1,anneau.y+Vdecalage-1) ; put(character'val(15)) ; set_foreground(Couleur_Texte) ; end print_anneau ; -------------------------------- -- PROGRAMME DE JEU -- -------------------------------- procedure Erreur(Message : in String) is begin Goto_XY(HDecalage+5,VDecalage+Hauteur+2) ; set_background(Couleur_Ecran) ; set_foreground(light_Red) ; Put(Message) ; delay 1.0 ; end Erreur ; procedure un_tour(Serpent : in out T_Serpent) is choix_effectue : boolean := false ; Temps : constant Time := clock ; New_direction : T_Coord ; begin while clock-temps<duree loop if not choix_effectue and then key_available and then character'pos(get_key) = 0 then touche := get_key ; case character'pos(touche) is when 72 => New_direction := (0,-1) ; --haut when 75 => New_direction := (-1,0) ; --gauche when 77 => New_direction := (1,0) ; --droite when 80 => New_direction := (0,1) ; --bas when others => null ; end case ; if New_direction.x /= Serpent.direction.x and New_direction.y /= Serpent.direction.y then Serpent.direction := New_direction ; choix_effectue := true ; end if ; end if ; end loop ; end un_tour ; procedure game(Serpent : in out T_Serpent) is germe : generator ; Anneau : T_Coord ; begin reset(germe) ; Anneau := generer(germe,Serpent) ; Print_anneau(Anneau) ; loop un_tour(Serpent) ; --test pour savoir si le serpent grandit ou avance if a_mange(serpent,anneau) then grow(serpent) ; Score := Score + 100 ; Print_score ; Anneau := generer(germe,serpent) ; Print_anneau(Anneau) ; else move(serpent) ; end if ; --test pour savoir si le serpent meurt if est_sorti(serpent) then Erreur("Vous " & character'val(136) & "tes sortis !") ; exit ; elsif est_mordu(serpent) then Erreur("Vous vous " & character'val(136) & "tes mordu !") ; exit ; end if ; end loop ; end game ; end Snake_Programs ; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | with Snake_Variables ; use Snake_Variables ; with ada.Numerics.Discrete_Random ; with ada.Containers.Doubly_Linked_Lists ; use ada.Containers ; package Snake_Programs is package P_Liste is new Ada.Containers.Doubly_Linked_Lists(T_coord) ; use P_Liste ; package P_Aleatoire is new ada.Numerics.Discrete_Random(Intervalle_Alea) ; use P_Aleatoire ; Type T_Serpent is record corps : List ; Curseur : Cursor ; direction : T_Coord ; end record ; procedure print_plateau ; procedure print_score ; procedure Init_Serpent(Serpent : in out T_Serpent) ; procedure move(Serpent : in out T_Serpent) ; procedure grow(Serpent : in out T_Serpent) ; function est_sorti(Serpent : T_Serpent) return boolean ; function est_mordu(Serpent : T_Serpent) return boolean ; procedure print_tete(Serpent : in T_Serpent) ; procedure print_corps(nb : natural) ; procedure print_serpent(Serpent : in out T_Serpent) ; procedure efface_queue(Serpent : in out T_Serpent) ; function generer(germe : generator ; Serpent : T_Serpent) return T_Coord ; procedure print_anneau(anneau : T_Coord) ; procedure Erreur(Message : in String) ; procedure un_tour(Serpent : in out T_Serpent) ; procedure game(Serpent : in out T_Serpent) ; end Snake_Programs ; |
Enfin, le package servant à l'affichage de mes écrans de titre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | with ada.text_IO ; use ada.Text_IO ; with NT_Console ; use NT_Console ; With Snake_Variables ; use Snake_Variables ; package body Snake_screen is procedure print_line(line : string) is begin for i in line'range loop case line(i) is when 'R' => set_background(Red) ; put(' ') ; set_background(Couleur_Ecran) ; when 'G' => set_background(Green) ; put(' ') ; set_background(Couleur_Ecran) ; when 'Y' => set_background(Yellow) ; put(' ') ; set_background(Couleur_Ecran) ; when '#' => set_background(Black) ; put(' ') ; set_background(Couleur_Ecran) ; when others => put(' ') ; end case ; end loop ; new_line ; end print_line ; procedure print_fichier(name : string) is F : file_type ; begin open(F,In_File,name) ; clear_screen(couleur_ecran) ; set_background(Couleur_Ecran) ; while not end_of_file(f) and not end_of_page(f) loop print_line(get_line(f) ) ; end loop ; close(f) ; end print_fichier; procedure print_ecran(Ecran : T_Ecran) is begin case Ecran is when SNAKE => print_fichier("Snake.pic") ; delay 2.5 ; when START => print_fichier("Start.pic") ; delay 1.0 ; when READY => print_fichier("Ready.pic") ; delay 1.5 ; end case ; end print_ecran; end Snake_Screen ; |
1 2 3 4 5 6 7 8 | package Snake_Screen is type T_Ecran is(START,READY,SNAKE) ; procedure print_line(line : string) ; procedure print_fichier(name : string) ; procedure print_ecran(Ecran : T_Ecran) ; end Snake_Screen ; |
Pour finir, je vous transmets également les fichiers .pic (en fait des fichiers textes) servant de support à ces fameux écrans titres (je suis preneur pour tout écran titre pouvant remplacer celui qui ressemble vaguement à un serpent ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | . GGGGGGGG Y YG G YG Y YG GY GG GY GGY GGGGGGGGGGGY YGGYGYGYGYGYG GGGGY GGY GGY GG GG YGG GGGG YYGGGGGG YYGGGGGG YYGGGGG YYYG |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | . #### ##### # #### # # ### # # # # # # # # # # # # # # # # # # # # # #### ### # # # # # # # # # ##### # # # # # # # # # # # # # # ##### # # #### # # |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | . ### ##### # #### ##### # # # # # # # # # # # # # # # # # # # # # # # ### # # # #### # # # # # # ##### # # # # # # # # # # # # # # ### # # # # # # # # # |
Pistes d'amélioration :
- Proposer différent niveaux de difficulté : en faisant varier la vitesse, la taille de l'aire de jeu…
- Ajouter des adversaires ou des obstacles.
- Proposant l'enregistrement des meilleurs scores.
- Proposer des bonus comme des anneaux comptant le double de points ou permettant de réduire votre taille.