Task: 2048


C++: #include <time.h>
#include <iostream>
#include <string>
#include <iomanip>
#include <cstdlib>

typedef unsigned int uint;
using namespace std;
enum movDir { UP, DOWN, LEFT, RIGHT };

class tile
{
public:
    tile() : val( 0 ), blocked( false ) {}
    uint val;
    bool blocked;
};

class g2048
{
public:
    g2048() : done( false ), win( false ), moved( true ), score( 0 ) {}
    void loop()
    {
	addTile(); 
	while( true )
	{
	    if( moved ) addTile();
	    drawBoard(); 
	    if( done ) break;
	    waitKey();
	}
	string s = "Game Over!";
	if( win ) s = "You've made it!";
	cout << s << endl << endl;
    }
private:
    void drawBoard()
    {
	system( "cls" );
	cout << "SCORE: " << score << endl << endl;
	for( int y = 0; y < 4; y++ )
	{
	    cout << "+------+------+------+------+" << endl << "| ";
	    for( int x = 0; x < 4; x++ )
	    {
		if( !board[x][y].val ) cout << setw( 4 ) << " ";
		else cout << setw( 4 ) << board[x][y].val;
		cout << " | ";
	    }
	    cout << endl;
	}
	cout << "+------+------+------+------+" << endl << endl;
    }
    void waitKey()
    {
	moved = false; char c; 
	cout << "(W)Up (S)Down (A)Left (D)Right "; cin >> c; c &= 0x5F;
	switch( c )
	{
	    case 'W': move( UP );break;
	    case 'A': move( LEFT ); break;
	    case 'S': move( DOWN ); break;
	    case 'D': move( RIGHT );
	}
	for( int y = 0; y < 4; y++ )
	    for( int x = 0; x < 4; x++ )
		board[x][y].blocked = false;
    }
    void addTile()
    {
	for( int y = 0; y < 4; y++ )
	    for( int x = 0; x < 4; x++ )
		if( !board[x][y].val )
		{
		    uint a, b;
		    do
		    { a = rand() % 4; b = rand() % 4; }
		    while( board[a][b].val );

		    int s = rand() % 100;
		    if( s > 89 ) board[a][b].val = 4;
		    else board[a][b].val = 2;
		    if( canMove() ) return;
		}
	done = true;
    }
    bool canMove()
    {
	for( int y = 0; y < 4; y++ )
	    for( int x = 0; x < 4; x++ )
		if( !board[x][y].val ) return true;

	for( int y = 0; y < 4; y++ )
	    for( int x = 0; x < 4; x++ )
	    {
		if( testAdd( x + 1, y, board[x][y].val ) ) return true;
		if( testAdd( x - 1, y, board[x][y].val ) ) return true;
		if( testAdd( x, y + 1, board[x][y].val ) ) return true;
		if( testAdd( x, y - 1, board[x][y].val ) ) return true;
	    }
	return false;
    }
    bool testAdd( int x, int y, uint v )
    {
	if( x < 0 || x > 3 || y < 0 || y > 3 ) return false;
	return board[x][y].val == v;
    }
    void moveVert( int x, int y, int d )
    {
	if( board[x][y + d].val && board[x][y + d].val == board[x][y].val && !board[x][y].blocked && !board[x][y + d].blocked  )
	{
	    board[x][y].val = 0;
	    board[x][y + d].val *= 2;
	    score += board[x][y + d].val;
	    board[x][y + d].blocked = true;
	    moved = true;
	}
	else if( !board[x][y + d].val && board[x][y].val )
	{
	    board[x][y + d].val = board[x][y].val;
	    board[x][y].val = 0;
	    moved = true;
	}
	if( d > 0 ) { if( y + d < 3 ) moveVert( x, y + d,  1 ); }
	else        { if( y + d > 0 ) moveVert( x, y + d, -1 ); }
    }
    void moveHori( int x, int y, int d )
    {
	if( board[x + d][y].val && board[x + d][y].val == board[x][y].val && !board[x][y].blocked && !board[x + d][y].blocked  )
	{
	    board[x][y].val = 0;
	    board[x + d][y].val *= 2;
	    score += board[x + d][y].val;
	    board[x + d][y].blocked = true;
	    moved = true;
	}
	else if( !board[x + d][y].val && board[x][y].val )
	{
	    board[x + d][y].val = board[x][y].val;
	    board[x][y].val = 0;
	    moved = true;
	}
	if( d > 0 ) { if( x + d < 3 ) moveHori( x + d, y,  1 ); }
	else        { if( x + d > 0 ) moveHori( x + d, y, -1 ); }
    }
    void move( movDir d )
    {
	switch( d )
	{
	    case UP:
	    	for( int x = 0; x < 4; x++ )
		{
		    int y = 1;
		    while( y < 4 )
		    { if( board[x][y].val ) moveVert( x, y, -1 ); y++;}
		}
		break;
	    case DOWN:
		for( int x = 0; x < 4; x++ )
		{
		    int y = 2;
		    while( y >= 0 )
		    { if( board[x][y].val ) moveVert( x, y, 1 ); y--;}
		}
		break;
	    case LEFT:
		for( int y = 0; y < 4; y++ )
		{
		    int x = 1;
		    while( x < 4 )
		    { if( board[x][y].val ) moveHori( x, y, -1 ); x++;}
		}
		break;
	    case RIGHT:
		for( int y = 0; y < 4; y++ )
		{
		    int x = 2;
		    while( x >= 0 )
		    { if( board[x][y].val ) moveHori( x, y, 1 ); x--;}
		}
	}
    }
    tile board[4][4];
    bool win, done, moved;
    uint score;
};
int main( int argc, char* argv[] )
{
    srand( static_cast<uint>( time( NULL ) ) );
    g2048 g; g.loop();
    return system( "pause" );
}










Ada: with Ada.Text_IO; use Ada.Text_IO;
with System.Random_Numbers;
procedure Play_2048 is
   
   type t_Keystroke is (Up, Down, Right, Left, Quit, Restart, Invalid);
   
   function Get_Immediate return Character is
   begin
      return Answer : Character do Ada.Text_IO.Get_Immediate(Answer);
      end return;
   end Get_Immediate;
   Arrow_Prefix : constant Character := Character'Val(224); 
   function Get_Keystroke return t_Keystroke 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);
 
   
   function Random_Int is new System.Random_Numbers.Random_Discrete(Integer);
   type    t_List  is array (Positive range <>) of Natural;
   subtype t_Row   is t_List (1..4);
   type    t_Board is array  (1..4) of t_Row;
   Board     : t_Board;
   New_Board : t_Board;
   Blanks    : Natural;
   Score     : Natural;
   Generator : System.Random_Numbers.Generator;
 
   
   procedure Display_Board is
      Horizontal_Rule : constant String := "+
      function Center (Value : in String) return String is
        ((1..(2-(Value'Length-1)/2) => ' ') & 
         Value(Value'First+1..Value'Last)   & 
         (1..(2-Value'Length/2) => ' '));     
   begin
      Put_Line (Horizontal_Rule);
      for Row of Board loop
         for Cell of Row loop
            Put('|' & (if Cell = 0 then "    " else Center(Cell'Img)));
         end loop;
         Put_Line("|");
         Put_Line (Horizontal_Rule);
      end loop;
      Put_Line("Score =" & Score'Img);
   end Display_Board;
 
   
   procedure Add_Block is
      Block_Offset : Positive := Random_Int(Generator, 1, Blanks);
   begin
      Blanks := Blanks-1;
      for Row of Board loop
         for Cell of Row loop
            if Cell = 0 then
               if Block_Offset = 1 then
                  Cell := (if Random_Int(Generator,1,10) = 1 then 4 else 2);
                  return;
               else
                  Block_Offset := Block_Offset-1;
               end if;
            end if;
         end loop;
      end loop;
   end Add_Block;
 
   procedure Reset_Game is
   begin
      Board  := (others => (others => 0));
      Blanks := 16;
      Score  := 0;
      Add_Block;
      Add_Block;
   end Reset_Game;
 
   
   function HFlip (What : in t_Row) return t_Row is
     (What(4),What(3),What(2),What(1));
   function VFlip (What : in t_Board) return t_Board is
     (HFlip(What(1)),HFlip(What(2)),HFlip(What(3)),HFlip(What(4)));
   function Transpose (What : in t_Board) return t_Board is
   begin
      return Answer : t_Board do
         for Row in t_Board'Range loop
            for Column in t_Row'Range loop
               Answer(Column)(Row) := What(Row)(Column);
            end loop;
         end loop;
      end return;
   end Transpose;
 
   
   
   function Add_Blank (Delta_Score : in Natural) return t_List is
   begin
      Blanks := Blanks+1;
      Score  := Score+Delta_Score;
      return (1 => 0);
   end Add_Blank;
 
   function Move_Row (What : in t_List) return t_List is
     (if What'Length = 1 then What
      elsif What(What'First) = 0
      then Move_Row(What(What'First+1..What'Last)) & (1 => 0)
      else (1 => What(What'First)) & Move_Row(What(What'First+1..What'Last)));
 
   function Merge (What : in t_List) return t_List is
     (if What'Length <= 1 or else What(What'First) = 0 then What
      elsif What(What'First) = What(What'First+1)
      then (1 => 2*What(What'First)) & Merge(What(What'First+2..What'Last)) & Add_Blank(What(What'First))
      else (1 => What(What'First)) & Merge(What(What'First+1..What'Last)));
 
   function Move (What : in t_Board) return t_Board is
     (Merge(Move_Row(What(1))),Merge(Move_Row(What(2))),Merge(Move_Row(What(3))),Merge(Move_Row(What(4))));
 
begin
   System.Random_Numbers.Reset(Generator);
   
   Main_Loop: loop
      Reset_Game;
      Game_Loop: loop    
         Display_Board;
         case Get_Keystroke is
            when Restart => exit Game_Loop;
            when Quit    => exit Main_Loop;
            when Left    => New_Board := Move(Board);
            when Right   => New_Board := VFlip(Move(VFlip(Board)));
            when Up      => New_Board := Transpose(Move(Transpose(Board)));
            when Down    => New_Board := Transpose(VFlip(Move(VFlip(Transpose(Board)))));
            when others  => null;
         end case;
         
         if New_Board = Board then
            Put_Line ("Invalid move...");
         elsif (for some Row of New_Board => (for some Cell of Row => Cell = 2048)) then
            Display_Board;
            Put_Line ("Win !");
            exit Main_Loop;
         else
            Board := New_Board;
            Add_Block; 
            if Blanks = 0
               and then (for all Row in 1..4 =>
                           (for all Column in 1..3 =>
                                (Board(Row)(Column) /= Board(Row)(Column+1))))
               and then (for all Row in 1..3 =>
                           (for all Column in 1..4 =>
                                (Board(Row)(Column) /= Board(Row+1)(Column)))) then
               Display_Board;
               Put_Line ("Lost !");
               exit Main_Loop;
            end if;
         end if;
      end loop Game_Loop;
   end loop Main_Loop;
end Play_2048;