Task: Snake


C++: #include <windows.h>
#include <ctime>
#include <iostream>
#include <string>

const int WID = 60, HEI = 30, MAX_LEN = 600;
enum DIR { NORTH, EAST, SOUTH, WEST };

class snake {
public:
    snake() {
        console = GetStdHandle( STD_OUTPUT_HANDLE ); SetConsoleTitle( "Snake" ); 
        COORD coord = { WID + 1, HEI + 2 }; SetConsoleScreenBufferSize( console, coord );
        SMALL_RECT rc = { 0, 0, WID, HEI + 1 }; SetConsoleWindowInfo( console, TRUE, &rc );
        CONSOLE_CURSOR_INFO ci = { 1, false }; SetConsoleCursorInfo( console, &ci );
    }
    void play() {
        std::string a;
        while( 1 ) {
            createField(); alive = true;
            while( alive ) { drawField(); readKey(); moveSnake(); Sleep( 50 ); }
            COORD c = { 0, HEI + 1 }; SetConsoleCursorPosition( console, c );
            SetConsoleTextAttribute( console, 0x000b );
            std::cout << "Play again [Y/N]? "; std::cin >> a;
            if( a.at( 0 ) != 'Y' && a.at( 0 ) != 'y' ) return;
        }
    }
private:
    void createField() {
        COORD coord = { 0, 0 }; DWORD c;
        FillConsoleOutputCharacter( console, ' ', ( HEI + 2 ) * 80, coord, &c );
        FillConsoleOutputAttribute( console, 0x0000, ( HEI + 2 ) * 80, coord, &c );
        SetConsoleCursorPosition( console, coord );
        int x = 0, y = 1; for( ; x < WID * HEI; x++ ) brd[x] = 0;
        for( x = 0; x < WID; x++ ) {
            brd[x] = brd[x + WID * ( HEI - 1 )] = '+';
        }
        for( ; y < HEI; y++ ) {
            brd[0 + WID * y] = brd[WID - 1 + WID * y] = '+';
        }
        do {
            x = rand() % WID; y = rand() % ( HEI >> 1 ) + ( HEI >> 1 );
        } while( brd[x + WID * y] );
        brd[x + WID * y] = '@';
        tailIdx = 0; headIdx = 4; x = 3; y = 2;
        for( int c = tailIdx; c < headIdx; c++ ) {
            brd[x + WID * y] = '#';
            snk[c].X = 3 + c; snk[c].Y = 2;
        }
        head = snk[3]; dir = EAST; points = 0;
    }
    void readKey() {
        if( GetAsyncKeyState( 39 ) & 0x8000 ) dir = EAST;
        if( GetAsyncKeyState( 37 ) & 0x8000 ) dir = WEST;
        if( GetAsyncKeyState( 38 ) & 0x8000 ) dir = NORTH;
        if( GetAsyncKeyState( 40 ) & 0x8000 ) dir = SOUTH;
    }
    void drawField() {
        COORD coord; char t;
        for( int y = 0; y < HEI; y++ ) {
            coord.Y = y;
            for( int x = 0; x < WID; x++ ) {
                t = brd[x + WID * y]; if( !t ) continue;
                coord.X = x; SetConsoleCursorPosition( console, coord );
                if( coord.X == head.X && coord.Y == head.Y ) {
                    SetConsoleTextAttribute( console, 0x002e );
                    std::cout << 'O'; SetConsoleTextAttribute( console, 0x0000 );
                    continue;
                }
                switch( t ) {
                    case '#': SetConsoleTextAttribute( console, 0x002a ); break;
                    case '+': SetConsoleTextAttribute( console, 0x0019 ); break;
                    case '@': SetConsoleTextAttribute( console, 0x004c ); break;
                }
                std::cout << t; SetConsoleTextAttribute( console, 0x0000 );
            }
        }
        std::cout << t; SetConsoleTextAttribute( console, 0x0007 );
        COORD c = { 0, HEI }; SetConsoleCursorPosition( console, c );
        std::cout << "Points: " << points;
    }
    void moveSnake() {
        switch( dir ) {
            case NORTH: head.Y--; break;
            case EAST: head.X++; break;
            case SOUTH: head.Y++; break;
            case WEST: head.X--; break;
        }
        char t = brd[head.X + WID * head.Y];
        if( t && t != '@' ) { alive = false; return; }
        brd[head.X + WID * head.Y] = '#';
        snk[headIdx].X = head.X; snk[headIdx].Y = head.Y;
        if( ++headIdx >= MAX_LEN ) headIdx = 0;
        if( t == '@' ) {
            points++; int x, y;
            do {
                x = rand() % WID; y = rand() % ( HEI >> 1 ) + ( HEI >> 1 );
            } while( brd[x + WID * y] );
            brd[x + WID * y] = '@'; return;
        }
        SetConsoleCursorPosition( console, snk[tailIdx] ); std::cout << ' ';
        brd[snk[tailIdx].X + WID * snk[tailIdx].Y] = 0;
        if( ++tailIdx >= MAX_LEN ) tailIdx = 0;
    }
    bool alive; char brd[WID * HEI]; 
    HANDLE console; DIR dir; COORD snk[MAX_LEN];
    COORD head; int tailIdx, headIdx, points;
};
int main( int argc, char* argv[] ) {
    srand( static_cast<unsigned>( time( NULL ) ) );
    snake s; s.play(); return 0;
}










Ada: 



with Ada.Containers; use Ada.Containers;
with Ada.Containers.Vectors;

package Snake is
   
   
   
   type directions is (Up, Down, Right, Left, Quit, Restart, Invalid);
   
   
   function Get_Immediate return Character;

   Arrow_Prefix : constant Character := Character'Val(224); 

   function Get_Keystroke return directions;
   
   
   
   type gameBoard is array (Natural range <>, Natural range <>) of Character;
   
   
   procedure init;
   
   
   procedure run;  
   
   
   procedure Display_Board;
   
   procedure ClearBoard;
   
   
   type coord is tagged record
      X,Y : Integer;
   end record;

   
   package snakeBody is new Ada.Containers.Vectors (Natural, coord);
   use snakeBody;
   
   
   function moveSnake return Boolean;
   
   
   procedure generateFood;
   
   
   procedure addDataToBoard;
   
   
   function getRandomInteger(upperBound : Integer) return Integer;
   
private
   
   width, height : Positive := 10;
   board : gameBoard := (0 .. (width+1) => (0 .. (height+1) => ' '));
   snake : Vector := (1,1) & (2,1) & (3,1);
   snakeDirection : directions := Right;
   
   food : coord := (5,5);

end Snake;




with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Discrete_Random;

package body Snake is
   
   
   
   function Get_Immediate return Character is
   begin
      return Answer : Character do Ada.Text_IO.Get_Immediate(Answer);
      end return;
   end Get_Immediate;

   
   
   function Get_Keystroke return directions is
     (case Get_Immediate is
         when 'Q' | 'q' => Quit,
         when 'R' | 'r' => Restart,
         when 'W' | 'w' => Left,
         when 'A' | 'a' => Up,
         when 'S' | 's' => Down,
         when 'D' | 'd' => Right,
         
         when Arrow_Prefix => (case Character'Pos(Get_Immediate) is
                                  when 72 => Up,
                                  when 75 => Left,
                                  when 77 => Right,
                                  when 80 => Down,
                                  when others => Invalid),
         
         when ASCII.ESC => (case Get_Immediate is
                               when '[' => (case Get_Immediate is
                                               when 'A' => Up,
                                               when 'B' => Down,
                                               when 'C' => Right,
                                               when 'D' => Left,
                                               when others => Invalid),
                               when others => Invalid),
         when others => Invalid);
   
   
   procedure init is
   begin
      
      for Row in 0 .. width+1 loop
         for Column in 0 .. height+1 loop
            
            if Row = 0 or Row = width+1 then
               board(Row,Column) := '#';
               
            else
               if Column = 0 or Column = height+1 then
                  board(Row,Column) := '#';
                  
               else
                  board(Row,Column) := ' ';
                  
               end if;
            end if;
            
         end loop;
      end loop;  
   end;

   
   procedure run is
      
      task T;
      task body T is
      begin
         loop
            snakeDirection := Get_Keystroke;
         end loop;
      end T;
      
   begin
      
      init;
      loop
         exit when snakeDirection = Quit;
      
         if moveSnake = False then
            Put_Line("GAME OVER!!!");
            Put_Line("Your score is:" & Length(snake)'Image);
            exit;
         end if;
      
      
         Display_Board;
         delay 0.7;
         
      end loop;
   end run;
   
   
   procedure Display_Board is
   begin
      
      for Row in 0 .. width+1 loop
         for Column in 0 .. height+1 loop
            Put(board(Row, Column));
         end loop;
         New_Line;
      end loop; 
      
   end Display_Board;
   
   
   procedure ClearBoard is
   begin
      
      for Row in 1 .. width loop
         for Column in 1 .. height loop
            board(Row, Column) := ' ';
         end loop;
      end loop; 
      
   end ClearBoard;
   
   
   function moveSnake return Boolean is
      colision : Boolean := False;
      headCoord : coord := Snake.First_Element;
      addSnakePart : Boolean := False;
   begin
      
      case snakeDirection is
         when Up => headCoord.X := headCoord.X - 1;
         when Down => headCoord.X := headCoord.X + 1;
         when Right => headCoord.Y := headCoord.Y + 1;
         when Left => headCoord.Y := headCoord.Y - 1;
         when others => null;
      end case;
      
      if headCoord.Y = height+1 then
         return False;
      end if;
      
      if headCoord.Y = 0 then
         return False;
      end if;
      
      if headCoord.X = width+1 then
         return False;
      end if;
      
      if headCoord.X = 0 then
         return False;
      end if;
      
      for index in snake.Iterate loop
         if headCoord = snake(To_Index(index)) then
            return False;
         end if;
      end loop;

      snake.Prepend(headCoord);
      
      if headCoord /= food then
         snake.Delete_Last;
      else
         food := (0,0);
         generateFood;
      end if;
      
      addDataToBoard;

      return True;
   end moveSnake;
   
   
   procedure generateFood is
   begin
      if food.X = 0 or food.Y = 0 then
         food := (getRandomInteger(width), getRandomInteger(height));
      end if;
   end generateFood;
   
   
   procedure addDataToBoard is
   begin
      ClearBoard;
      
      board(food.X, food.Y) := '*';
      
      for index in snake.Iterate loop
         board(snake(To_Index(index)).X, snake(To_Index(index)).Y) := 'o';
      end loop;
      
   end addDataToBoard;

   
   function getRandomInteger(upperBound : Integer) return Integer is

      subtype Random_Range is Integer range 1 .. upperBound;

      package R is new
        Ada.Numerics.Discrete_Random (Random_Range);
      use R;

      G : Generator;
   begin
      Reset (G);
      return Random (G);
   end getRandomInteger;

end Snake;




with Snake;

procedure Main is
begin
   Snake.run;
end;