# 2048

2048
You are encouraged to solve this task according to the task description, using any language you may know.

Implement a 2D sliding block puzzle game where blocks with numbers are combined to add their values.

The rules are that on each turn the player must choose a direction (up, down, left or right) and all tiles move as far as possible in that direction, some more than others. Two adjacent tiles (in that direction only) with matching numbers combine into one bearing the sum of those numbers. A move is valid when at least one tile can be moved, if only by combination. A new tile with the value of 2 is spawned at the end of each turn at a randomly chosen empty square, if there is one. To win the player must create a tile with the number 2048. The player loses if no valid moves are possible.

The name comes from the popular open-source implementation of this game mechanic, 2048.

Requirements:

• "Non-greedy" movement. The tiles that were created by combining other tiles should not be combined again during the same turn (move). That is to say that moving the tile row of
``` [2][2][2][2]
```

to the right should result in

``` ......[4][4]
```

and not

``` .........[8]
```
• "Move direction priority". If more than one variant of combining is possible, move direction shows one that will take effect. For example, moving the tile row of
``` ...[2][2][2]
```

to the right should result in

``` ......[2][4]
```

and not

``` ......[4][2]
```
• Adding a new tile on a blank space. Most of the time new "2" is to be added and occasionally (10% of the time) - "4"
• Check for valid moves. The player shouldn't be able to skip their turn by trying a move that doesn't change the board.
• Win condition.
• Lose condition.

Works with: GNAT
`with Ada.Text_IO; use Ada.Text_IO;with System.Random_Numbers;procedure Play_2048 is   -- ----- Keyboard management   type t_Keystroke is (Up, Down, Right, Left, Quit, Restart, Invalid);   -- Redefining this standard procedure as function to allow Get_Keystroke as an expression function   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); -- works for windows   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,         -- Windows terminal         when Arrow_Prefix => (case Character'Pos(Get_Immediate) is                                  when 72 => Up,                                  when 75 => Left,                                  when 77 => Right,                                  when 80 => Down,                                  when others => Invalid),         -- Unix escape sequences         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);    -- ----- Game data   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;    -- ----- Displaying the board   procedure Display_Board is      Horizontal_Rule : constant String := "+----+----+----+----+";      function Center (Value : in String) return String is        ((1..(2-(Value'Length-1)/2) => ' ') & -- Add leading spaces         Value(Value'First+1..Value'Last)   & -- Trim the leading space of the raw number image         (1..(2-Value'Length/2) => ' '));     -- Add trailing spaces   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;    -- ----- Game mechanics   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;    -- Moving and merging will always be performed leftward, hence the following transforms   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;    -- For moving/merging, recursive expression functions will be used, but they   -- can't contain statements, hence the following sub-function used by Merge   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);   Reset_Game;   Main_Game_Loop: loop      Display_Board;      case Get_Keystroke is         when Restart => Reset_Game;         when Quit    => exit Main_Game_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_Game_Loop;      else         Board := New_Board;         Add_Block; -- OK since the board has changed         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_Game_Loop;         end if;      end if;   end loop Main_Game_Loop;end Play_2048;`
Output:
```+----+----+----+----+
|  2 | 16 |  2 |  2 |
+----+----+----+----+
| 64 |    |    |    |
+----+----+----+----+
|  4 |    |    |    |
+----+----+----+----+
|    |    |    |    |
+----+----+----+----+
Score = 184```

## ALGOL 68

` main:(INT side = 4;INT right = 1, up = 2, left = 3, down = 4;[]CHAR direction letters = "ruld";[]STRING direction descriptions = ("right", "up", "left", "down"); MODE BOARD = REF[,]INT;MODE CELL = REF INT; OP = = (BOARD a, BOARD b) BOOL:  (FOR i TO side DO FOR j TO side DO IF a[i,j] /= b[i,j] THEN mismatch FI OD OD;  TRUE EXIT  mismatch: FALSE); PROC traverse board = (BOARD board, PROC(CELL)VOID callback) VOID:  FOR i FROM 1 LWB board TO 1 UPB board DO    FOR j FROM 2 LWB board TO 2 UPB board DO      callback(board[i,j])  OD OD; PROC count blanks = (BOARD board) INT:  (INT count := 0;   traverse board(board, (CELL c)VOID: IF c = 0 THEN count +:= 1 FI);   count); PROC nth blank = (BOARD board, INT nth) CELL:  (CELL result;   INT count := 0;   traverse board(board, (CELL c)VOID:                           (IF c = 0 THEN count +:= 1 FI;                            IF count = nth THEN                              result := c; return                            FI));   return: result); PROC add new number = (BOARD board) VOID:  (INT nblanks = count blanks(board);   INT number   := (random >= .9 | 4 | 2);   INT position := ENTIER (random * nblanks) + 1;    nth blank(board, position) := number); PROC shift = (REF[]INT row, BOOL to the end) VOID:  (INT from = (to the end | UPB row | LWB row),       to   = (to the end | LWB row | UPB row),       dir  = (to the end | -1 | 1);   FOR i FROM from + dir BY dir TO to  DO     IF row[i] /= 0 THEN       INT blank := 0;       FOR j FROM i - dir BY -dir TO from WHILE row[j] = 0 DO         blank := j       OD;       IF blank /= 0 THEN         row[blank] := row[i];         row[i] := 0       FI     FI   OD); PROC combine = (REF[]INT row, BOOL to the end) VOID:  (INT from = (to the end | UPB row | LWB row),       to   = (to the end | LWB row | UPB row),       dir  = (to the end | -1 | 1);   FOR i FROM from BY dir TO to - dir DO     IF row[i] /= 0 AND row[i] = row[i+dir] THEN       row[i] *:= 2;       row[i+dir] := 0     FI   OD); PROC move = (BOARD board, INT direction) VOID:  FOR i TO side DO    CASE direction IN      # right # (shift(board[i,], TRUE);  combine(board[i,], TRUE);  shift(board[i,], TRUE)),      # up    # (shift(board[,i], FALSE); combine(board[,i], FALSE); shift(board[,i], FALSE)),      # left  # (shift(board[i,], FALSE); combine(board[i,], FALSE); shift(board[i,], FALSE)),      # down  # (shift(board[,i], TRUE);  combine(board[,i], TRUE);  shift(board[,i], TRUE))    ESAC  OD; PROC print board = (BOARD board)VOID:  (FOR i FROM 1 LWB board TO 1 UPB board DO     print("+");     FOR j FROM 2 LWB board TO 2 UPB board DO print("------+") OD;     print((new line, "|"));     FOR j FROM 2 LWB board TO 2 UPB board DO       print(((board[i,j] = 0 | "     " | whole(board[i,j],-5)), " |"))     OD;     print(new line)   OD;   print("+"); FOR j FROM 2 LWB board TO 2 UPB board DO print("------+") OD;   print(new line)  ); PROC score = (BOARD board) INT:  (INT result := 0;   traverse board(board, (CELL c)VOID: result +:= c);   result); PROC join = ([]STRING strings, STRING joiner) STRING:  IF UPB strings > 0 THEN    STRING result := strings[1];    FOR i FROM 2 TO UPB strings DO result +:= joiner +:= strings[i] OD;    result  ELSE    ""  FI; BOARD board = LOC [side,side]INT;BOARD previous = LOC [side,side]INT; traverse board(board, (CELL c)VOID: c := 0); # start with two numbers #TO 2 DO add new number(board) OD; # play! #STRING prompt := "enter one of [" + direction letters + "] (for " + join(direction descriptions, "/") + "): ";DO  CHAR key;  INT dir;  print board(board);  print(("score: ", whole(score(board),0), new line));  WHILE    print(prompt);    read((key, new line));    NOT char in string(key, dir, direction letters)  DO SKIP OD;  previous := board;  move(board, dir);  IF count blanks(board) = 0 THEN lose FI;  traverse board(board, (CELL c)VOID: IF c = 2048 THEN win FI);  IF previous = board THEN    print(("try again!", new line))  ELSE    add new number(board)  FIOD; win: print board(board); print(("you win!", new line)) EXITlose: print(("you lose!", new line))) `

## AutoHotkey

`Grid := [], s := 16, w := h := S * 4.5Gui, font, s%s%Gui, add, text, y1loop, 4{	row := A_Index	loop, 4	{		col := A_Index		if col = 1			Gui, add, button, v%row%_%col% xs  y+1 w%w% h%h% -TabStop, % Grid[row,col] := 0		else			Gui, add, button, v%row%_%col% x+1 yp  w%w% h%h%  -TabStop, % Grid[row,col] := 0	}}Gui, show,, 2048;------------------------------ Start:for row, obj in Grid	for col, val in obj		Grid[row,col] := 0 Grid[1,1]:=2ShowGrid()return ;------------------------------GuiClose:ExitAppreturn;------------------------------#IfWinActive, 2048;------------------------------up::move := falseloop, 4{	col := A_Index	Loop, 3	{		row := A_Index		if Grid[row, col] && (Grid[row, col] = Grid[row+1, col])			Grid[row, col] *=2	, Grid[row+1, col] := 0, move := true	}} loop, 4{	row := A_Index	loop, 4	{		col := A_Index		loop, 4			if !Grid[row, col]				loop, 3					if !Grid[row, col] && Grid[row+A_Index, col]					{						Grid[row, col] := Grid[row+A_Index, col]	, Grid[row+A_Index, col] := 0, move := true						if (Grid[row, col] = Grid[row-1, col])							Grid[row-1, col] *=2	, Grid[row, col] := 0, move := true					}	}}gosub, AddNewreturn;------------------------------Down::move := falseloop, 4{	col := A_Index	Loop, 3	{		row := 5-A_Index		if Grid[row, col] && (Grid[row, col] = Grid[row-1, col])			Grid[row, col] *=2	, Grid[row-1, col] := 0, move := true	}} loop, 4{	row := 5-A_Index	loop, 4	{		col := A_Index		loop, 4			if !Grid[row, col]				loop, 3					if !Grid[row, col] && Grid[row-A_Index, col]					{						Grid[row, col] := Grid[row-A_Index, col]	, Grid[row-A_Index, col] := 0, move := true						if (Grid[row, col] = Grid[row+1, col])							Grid[row+1, col] *=2	, Grid[row, col] := 0, move := true					}	}}gosub, AddNewreturn;------------------------------Left::move := falseloop, 4{	row := A_Index	Loop, 3	{		col := A_Index		if Grid[row, col] && (Grid[row, col] = Grid[row, col+1])			Grid[row, col] *=2	, Grid[row, col+1] := 0, move := true	}} loop, 4{	col := A_Index	loop, 4	{		row := A_Index		loop, 4			if !Grid[row, col]				loop, 3					if !Grid[row, col] && Grid[row, col+A_Index]					{						Grid[row, col] := Grid[row, col+A_Index]	, Grid[row, col+A_Index] := 0, move := true						if (Grid[row, col] = Grid[row, col-1])							Grid[row, col-1] *=2	, Grid[row, col] := 0, move := true					} 	}}gosub, AddNewreturn;------------------------------Right::move := falseloop, 4{	row := A_Index	Loop, 3	{		col := 5-A_Index		if Grid[row, col] && (Grid[row, col] = Grid[row, col-1])			Grid[row, col] *=2	, Grid[row, col-1] := 0, move := true	}} loop, 4{	col := 5-A_Index	loop, 4	{		row := A_Index		loop, 4			if !Grid[row, col]				loop, 3					if !Grid[row, col] && Grid[row, col-A_Index]					{						Grid[row, col] := Grid[row, col-A_Index]	, Grid[row, col-A_Index] := 0, move := true						if (Grid[row, col] = Grid[row, col+1])							Grid[row, col+1] *=2	, Grid[row, col] := 0, move := true					}	}}gosub, AddNewreturn ;------------------------------#IfWinActive;------------------------------AddNew:if EndOfGame(){	MsgBox Done `nPress OK to retry	goto start}return ;------------------------------EndOfGame(){	global	if Move		AddRandom()	ShowGrid()	for row, obj in Grid		for col, val in obj			if !grid[row,col]				return 0 	for row, obj in Grid		for col, val in obj			if (grid[row,col] = grid[row+1,col]) || (grid[row,col] = grid[row-1,col]) || (grid[row,col] = grid[row,col+1]) || (grid[row,col] = grid[row,col-1])				return 0	return 1} ;------------------------------ShowGrid(){	global Grid	for row, obj in Grid		for col, val in obj		{			GuiControl,, %row%_%col%, %val%			if val				GuiControl, Show, %row%_%col%			else				GuiControl, Hide, %row%_%col%		}} ;------------------------------AddRandom(){	global Grid	ShowGrid()	Sleep, 200	for row, obj in Grid		for col, val in obj			if !grid[row,col]				list .= (list?"`n":"") row "," col	Sort, list, random	Rnd := StrSplit(list, "`n").1	Grid[StrSplit(rnd, ",").1, StrSplit(rnd, ",").2] := 2};------------------------------`

## Batch File

`::2048 Game Task from RosettaCode.org::Batch File Implementation @echo offsetlocal enabledelayedexpansioncls :begin_game	%== Set variables ==%set "score=0"set "won=0"set "SUP_score=0"for /l %%A in (1,1,4) do for /l %%B in (1,1,4) do set /a "X_%%A%%B=0" call :addtilecall :addtile 	%== Main game loop ==%:main_loop	set "changed=0"	call :display	echo(	echo Keys: WASD (Slide Movement^), N (New game^), P (Exit^) 	%== Get Keypress ==%	set "key="	for /f "delims=" %%? in ('xcopy /w "%~f0" "%~f0" 2^>nul') do if not defined key set "key=%%?"	set "key=%key:~-1%" 	%== Process keypress ==%	if /i "!key!"=="W" (		for /l %%? in (1,1,4) do call :slide X_1%%? X_2%%? X_3%%? X_4%%?	)	if /i "!key!"=="A" (		for /l %%? in (1,1,4) do call :slide X_%%?1 X_%%?2 X_%%?3 X_%%?4	)	if /i "!key!"=="S" (		for /l %%? in (1,1,4) do call :slide X_4%%? X_3%%? X_2%%? X_1%%?	)	if /i "!key!"=="D" (		for /l %%? in (1,1,4) do call :slide X_%%?4 X_%%?3 X_%%?2 X_%%?1	)	if /i "!key!"=="N" goto :begin_game	if /i "!key!"=="P" exit /b 	%== Check if the board changed ==%	if %changed% neq 0 call :addtile 	%== Check if already won ==%	if %won% equ 1 (		set "msg=Nice one... You WON^!^!"		goto :gameover	) 	%== Check for lose condition ==%	set /a "real_blanks=blank_count-1"	if %real_blanks% leq 0 (		for /l %%A in (1,1,4) do for /l %%B in (1,1,4) do set "TRY_%%A%%B=!X_%%A%%B!"		set "TRY_changed=%changed%" & set "changed=0"		set "SUP_score=1"		for /l %%? in (1,1,4) do call :slide TRY_%%?1 TRY_%%?2 TRY_%%?3 TRY_%%?4		for /l %%? in (1,1,4) do call :slide TRY_1%%? TRY_2%%? TRY_3%%? TRY_4%%?		if !changed! equ 0 (			set "msg=No moves are possible... Game Over :("			goto :gameover		) else (set "changed=!TRY_changed!" & set "SUP_score=0")	)goto main_loop ::~~~~~~~~~~~~~~~~~~~~ Sub Procedures ~~~~~~~~~~~~~~~~~~~~::	%== Game Over xD ==%:gameovercall :displayecho(echo(!msg!echo(echo(Keys: N (New game^), P (Exit^) :key_loopset "key="for /f "delims=" %%? in ('xcopy /w "%~f0" "%~f0" 2^>nul') do if not defined key set "key=%%?"set "key=%key:~-1%"if /i "!key!"=="N" goto :begin_gameif /i "!key!"=="P" exit /bgoto :key_loop 	%== The main slider of numbers in tiles ==%:slideset "next="set "slide_1="set "slide_2="for %%? in (%*) do if !%%?! neq 0 set "slide_1=!slide_1! !%%?!"for %%? in (!slide_1!) do (	set "scan=%%?"	if "!scan!"=="!next!" (		set /a "next*=2"		if !SUP_score! equ 0 set /a "score+=!next!"		%== WINNING CONDITION!!! ==%		if "!next!" equ "2048" set "won=1"		set "scan="	)	set "slide_2=!slide_2! !next!"	set "next=!scan!")set "slide_2=!slide_2! !next!"for /l %%? in (1,1,4) do set "final_%%?=0"set "cnt=0" & for %%? in (!slide_2!) do if !cnt! lss 4 (	set /a "cnt+=1"	set "final_!cnt!=%%?")if not "!%1!!%2!!%3!!%4!"=="!final_1!!final_2!!final_3!!final_4!" set "changed=1"set "cnt=0" & for %%? in (%*) do (	set /a "cnt+=1"	set /a "%%?=final_!cnt!")goto :EOF 	%== Add number to tile ==%:addtileset "blank_list="set "blank_count=0"for /l %%A in (1,1,4) do for /l %%B in (1,1,4) do (	if !X_%%A%%B! equ 0 (		set "blank_list=!blank_list!X_%%A%%B"		set /a blank_count+=1	))	set /a "pick_tile=(%random% %% %blank_count%)*4"set /a "rnd=%random%%%10+1"set "tile_new=!blank_list:~%pick_tile%,4!"if %rnd%==5 (set !tile_new!=4) else (set !tile_new!=2)goto :EOF 	%== Display the table ==%:displayclsecho 2048 Game in Batchecho(for /l %%A in (1,1,4) do (	for /l %%B in (1,1,4) do (		set "DX_%%A%%B=!X_%%A%%B!"		if !tile_new!==X_%%A%%B (set "DX_%%A%%B=  +!X_%%A%%B!") else (			if !X_%%A%%B! lss 1000 set "DX_%%A%%B= !DX_%%A%%B!"			if !X_%%A%%B! lss 100 set "DX_%%A%%B= !DX_%%A%%B!"			if !X_%%A%%B! lss 10 set "DX_%%A%%B= !DX_%%A%%B!"			if !X_%%A%%B! equ 0 set "DX_%%A%%B=    "		)	)		echo +----+----+----+----+		echo ^|!DX_%%A1!^|!DX_%%A2!^|!DX_%%A3!^|!DX_%%A4!^|)echo +----+----+----+----+echo(echo Score: %score%goto :EOF`
Output:
```2048 Game in Batch

+----+----+----+----+
|    |  +2|    |    |
+----+----+----+----+
|   4|    |    |    |
+----+----+----+----+
|   4|    |    |    |
+----+----+----+----+
|  16|   4|    |   2|
+----+----+----+----+

Score: 60

Keys: WASD (Slide Movement), N (New game), P (Exit)```

## BBC BASIC

`      SIZE = 4     : MAX = SIZE-1      Won% = FALSE : Lost% = FALSE      @% = 5      DIM Board(MAX,MAX),Stuck% 3       PROCBreed      PROCPrint      REPEAT        Direction = GET-135        IF Direction > 0 AND Direction < 5 THEN          Moved% = FALSE          PROCShift          PROCMerge          PROCShift          IF Moved% THEN PROCBreed : !Stuck%=0 ELSE ?(Stuck%+Direction-1)=-1 : Lost% = !Stuck%=-1          PROCPrint        ENDIF      UNTIL Won% OR Lost%      IF Won% THEN PRINT "You WON! :-)" ELSE PRINT "You lost :-("      END       REM -----------------------------------------------------------------------------------------------------------------------      DEF PROCPrint      FOR i = 0 TO SIZE*SIZE-1        IF Board(i DIV SIZE,i MOD SIZE) THEN PRINT Board(i DIV SIZE,i MOD SIZE); ELSE PRINT "    _";        IF i MOD SIZE = MAX THEN PRINT      NEXT      PRINT STRING\$(SIZE,"-----")      ENDPROC       REM ----------------------------------------------------------------------------------------------------------------------      DEF PROCShift      IF Direction = 2 OR Direction = 3 THEN loopend = MAX : step = -1 ELSE loopend = 0 : step = 1      FOR row = loopend TO MAX-loopend STEP step        zeros = 0        FOR col = loopend TO MAX-loopend STEP step          IF Direction < 3 THEN            IF Board(row,col) = 0 THEN zeros += step ELSE IF zeros THEN SWAP Board(row,col),Board(row,col-zeros) : Moved% = TRUE          ELSE            IF Board(col,row) = 0 THEN zeros += step ELSE IF zeros THEN SWAP Board(col,row),Board(col-zeros,row) : Moved% = TRUE          ENDIF        NEXT      NEXT      ENDPROC       REM -----------------------------------------------------------------------------------------------------------------------      DEF PROCMerge      IF Direction = 1 THEN loopend =   0 : rowoff =  0 : coloff =  1 : step =  1      IF Direction = 2 THEN loopend = MAX : rowoff =  0 : coloff = -1 : step = -1      IF Direction = 3 THEN loopend = MAX : rowoff = -1 : coloff =  0 : step = -1      IF Direction = 4 THEN loopend =   0 : rowoff =  1 : coloff =  0 : step =  1      FOR row = loopend TO MAX-loopend-rowoff STEP step        FOR col = loopend TO MAX-loopend-coloff STEP step          IF Board(row,col) THEN IF Board(row,col) = Board(row+rowoff,col+coloff) THEN            Board(row,col) *= 2 : Board(row+rowoff,col+coloff) = 0            Moved% = TRUE            IF NOT Won% THEN Won% = Board(row,col)=2048          ENDIF        NEXT      NEXT      ENDPROC       REM -----------------------------------------------------------------------------------------------------------------------      DEF PROCBreed      cell = RND(SIZE*SIZE)-1      FOR i = 0 TO SIZE*SIZE-1        z = (cell+i) MOD (SIZE*SIZE)        IF Board(z DIV SIZE,z MOD SIZE) = 0 THEN Board(z DIV SIZE,z MOD SIZE) = 2-(RND(10)=1)*2 : EXIT FOR      NEXT      ENDPROC`
Output:
```    _    _    _    _
_    _    _    _
_    _    2    _
_    _    _    _
--------------------
_    _    _    _
_    _    _    _
2    _    _    _
_    2    _    _
--------------------
2    2    _    _
_    _    2    _
_    _    _    _
_    _    _    _
--------------------
4    2    _    _
2    _    _    _
_    _    _    _
_    _    _    _
--------------------
.
.
.
.
2    8    4    2
4    2   16    4
16    4    8   32
4   32    2    4
--------------------
You lost :-(```

## C

### Version 1

Supports limited colours through vt100 escape codes. Requires a posix machine for termios.h and unistd.h headers. Provides simplistic animations when moving and merging blocks.

` #include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <time.h>#include <unistd.h> #define D_INVALID -1#define D_UP       1#define D_DOWN     2#define D_RIGHT    3#define D_LEFT     4 const long values[] = {    0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048}; const char *colors[] = {    "39", "31", "32", "33", "34", "35", "36", "37", "91", "92", "93", "94"}; struct gamestate_struct__ {    int grid[4][4];    int have_moved;    long total_score;    long score_last_move;    int blocks_in_play;} game; struct termios oldt, newt; void do_draw(void){    printf("\033[2J\033[HScore: %ld", game.total_score);    if (game.score_last_move)        printf(" (+%ld)", game.score_last_move);    printf("\n");     for (int i = 0; i < 25; ++i)        printf("-");    printf("\n");     for (int y = 0; y < 4; ++y) {        printf("|");        for (int x = 0; x < 4; ++x) {            if (game.grid[x][y])                printf("\033[7m\033[%sm%*zd \033[0m|", colors[game.grid[x][y]],                        4, values[game.grid[x][y]]);            else                printf("%*s |", 4, "");        }        printf("\n");    }     for (int i = 0; i < 25; ++i) {        printf("-");    }    printf("\n");} void do_merge(int d){/* These macros look pretty scary, but mainly demonstrate some space saving */#define MERGE_DIRECTION(_v1, _v2, _xs, _xc, _xi, _ys, _yc, _yi, _x, _y)     \    do {                                                                    \        for (int _v1 = _xs; _v1 _xc; _v1 += _xi) {                          \            for (int _v2 = _ys; _v2 _yc; _v2 += _yi) {                      \                if (game.grid[x][y] && (game.grid[x][y] ==                  \                                    game.grid[x + _x][y + _y])) {           \                    game.grid[x][y] += (game.have_moved = 1);               \                    game.grid[x + _x][y + _y] = (0 * game.blocks_in_play--);\                    game.score_last_move += values[game.grid[x][y]];        \                    game.total_score += values[game.grid[x][y]];            \                }                                                           \            }                                                               \        }                                                                   \    } while (0)     game.score_last_move = 0;     switch (d) {        case D_LEFT:            MERGE_DIRECTION(x, y, 0, < 3, 1, 0, < 4, 1, 1, 0);            break;        case D_RIGHT:            MERGE_DIRECTION(x, y, 3, > 0, -1, 0, < 4, 1, -1, 0);            break;        case D_DOWN:            MERGE_DIRECTION(y, x, 3, > 0, -1, 0, < 4, 1, 0, -1);            break;        case D_UP:            MERGE_DIRECTION(y, x, 0, < 3, 1, 0, < 4, 1, 0, 1);            break;    } #undef MERGE_DIRECTION} void do_gravity(int d){#define GRAVITATE_DIRECTION(_v1, _v2, _xs, _xc, _xi, _ys, _yc, _yi, _x, _y) \    do {                                                                    \        int break_cond = 0;                                                 \        while (!break_cond) {                                               \            break_cond = 1;                                                 \            for (int _v1 = _xs; _v1 _xc; _v1 += _xi) {                      \                for (int _v2 = _ys; _v2 _yc; _v2 += _yi) {                  \                    if (!game.grid[x][y] && game.grid[x + _x][y + _y]) {    \                        game.grid[x][y] = game.grid[x + _x][y + _y];        \                        game.grid[x + _x][y + _y] = break_cond = 0;         \                        game.have_moved = 1;                                \                    }                                                       \                }                                                           \            }                                                               \            do_draw(); usleep(40000);                                       \        }                                                                   \    } while (0)     switch (d) {        case D_LEFT:            GRAVITATE_DIRECTION(x, y, 0, < 3, 1, 0, < 4, 1, 1, 0);            break;        case D_RIGHT:            GRAVITATE_DIRECTION(x, y, 3, > 0, -1, 0, < 4, 1, -1, 0);            break;        case D_DOWN:            GRAVITATE_DIRECTION(y, x, 3, > 0, -1, 0, < 4, 1, 0, -1);            break;        case D_UP:            GRAVITATE_DIRECTION(y, x, 0, < 3, 1, 0, < 4, 1, 0, 1);            break;    } #undef GRAVITATE_DIRECTION} int do_check_end_condition(void){    int ret = -1;    for (int x = 0; x < 4; ++x) {        for (int y = 0; y < 4; ++y) {            if (values[game.grid[x][y]] == 2048)                return 1;            if (!game.grid[x][y] ||                  ((x + 1 < 4) && (game.grid[x][y] == game.grid[x + 1][y])) ||                  ((y + 1 < 4) && (game.grid[x][y] == game.grid[x][y + 1])))                ret = 0;        }    }    return ret;} int do_tick(int d){    game.have_moved = 0;    do_gravity(d);    do_merge(d);    do_gravity(d);    return game.have_moved;} void do_newblock(void) {    if (game.blocks_in_play >= 16) return;     int bn = rand() % (16 - game.blocks_in_play);    int pn = 0;     for (int x = 0; x < 4; ++x) {        for (int y = 0; y < 4; ++y) {            if (game.grid[x][y])                continue;             if (pn == bn){                game.grid[x][y] = rand() % 10 ? 1 : 2;                game.blocks_in_play += 1;                return;            }            else {                ++pn;            }        }    }} int main(void){    /* Initialize terminal settings */    tcgetattr(STDIN_FILENO, &oldt);    newt = oldt;    newt.c_lflag &= ~(ICANON | ECHO);    tcsetattr(STDIN_FILENO, TCSANOW, &newt);     srand(time(NULL));    memset(&game, sizeof(game), 0);    do_newblock();    do_newblock();    do_draw();     while (1) {        int found_valid_key, direction, value;        do {            found_valid_key = 1;            direction       = D_INVALID;            value           = getchar();            switch (value) {                case 'h': case 'a':                    direction = D_LEFT;                    break;                case 'l': case 'd':                    direction = D_RIGHT;                    break;                case 'j': case 's':                    direction = D_DOWN;                    break;                case 'k': case 'w':                    direction = D_UP;                    break;                case 'q':                    goto game_quit;                    break;                case 27:                    if (getchar() == 91) {                        value = getchar();                        switch (value) {                            case 65:                                direction = D_UP;                                break;                            case 66:                                direction = D_DOWN;                                break;                            case 67:                                direction = D_RIGHT;                                break;                            case 68:                                direction = D_LEFT;                                break;                            default:                                found_valid_key = 0;                                break;                        }                    }                    break;                default:                    found_valid_key = 0;                    break;            }        }  while (!found_valid_key);         do_tick(direction);        if (game.have_moved != 0){                do_newblock();        }        do_draw();         switch (do_check_end_condition()) {            case -1:                goto game_lose;            case 1:                goto game_win;            case 0:                break;        }    }     if (0)game_lose:    printf("You lose!\n");    goto game_quit;    if (0)game_win:    printf("You win!\n");    goto game_quit;    if (0)game_quit:     /* Restore terminal settings */    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);    return 0;} `
Output:
```Score: 1100 (+4)
-------------------------
|  64 |  32 |  64 |  32 |
|  32 |  16 |   2 |   8 |
|  16 |   4 |   8 |   4 |
|   4 |   2 |   4 |   2 |
-------------------------
You lose!
```

### Version 2

`  #include <stdio.h>#include <string.h>#include <stdlib.h>#include <time.h>#define EMPTY_TILE 0#define ROWS 4#define COLUMNS 4  /*  *                                     GENERAL CONCEPT *  * How do you add up tiles when there is whitespace between them? * You sort the array so that there are no empty tiles between them while stacking them all to one side * then the addition function always adds up from left to right or up to bottom. It does not care * about the left movements or the down movement. This can be achieved by reversing the array * whenever the player chooses to move to the right or down, when the addition is finished * the array gets reversed back and its like it had been added from right to left or bottom to top * When the addition is done, the program scans for the number of empty tiles and uses that * in its selection of the next tile to be filled. 10% of times a tile gets occupied with a 4 **/   /*  * the remove_whitespace functions; it is pretty clear what they do. * they use a bubble short algorith to move the 0's or empty tiles to the end of the array * depending on the direction moved (without carring about going right or up **/ void remove_whitespace_horizontaly(int board[ROWS][COLUMNS], int rows, int columns){  int a = columns;  int tmp;    for (; a < COLUMNS - 1; ++a) {    tmp = board[rows][a];    board[rows][a] = board[rows][a+1];    board[rows][a+1] = tmp;  }} void remove_whitespace_verticaly(int board[ROWS][COLUMNS], int columns, int rows){  int a = rows;  int tmp;   for (; a < ROWS - 1; ++a) {    tmp = board[a][columns];    board[a][columns] = board[a+1][columns];    board[a+1][columns] = tmp;  }} /* * the add_tiles functions. those functions do the heavy work of adding the tiles and * taking care of special situations such as when adding two equal tiles a 0 gets generated * they are quite difficult to understand i think (which means not that you need to be too clever * but that i have done a poor job of creating them) and it took around 4 hours to get the  * proper result*/ void add_tiles_horizontaly(int board[ROWS][COLUMNS]){  int a, b, flag;   for (a = 0; a < ROWS; ++a) {    for (b = 0, flag = 0; b < COLUMNS - 1 && flag != 4; ++b) {      if (board[a][b] == EMPTY_TILE) {	remove_whitespace_horizontaly(board, a, b);	--b;	++flag;      }      else {	if (board[a][b+1] == EMPTY_TILE) {	  board[a][b+1] = board[a][b];	  board[a][b] = EMPTY_TILE;	  --b;	} else if (board[a][b] == board[a][b+1]) {	  board[a][b] += board[a][b+1];	  board[a][b+1] = EMPTY_TILE;	}      }    }  }} void add_tiles_verticaly(int board[ROWS][COLUMNS]){  int a, b, flag;   for (a = 0; a < COLUMNS; ++a) {    for (b = 0, flag = 0; b < ROWS-1 && flag != 4; ++b) {      if (board[b][a] == EMPTY_TILE) {	remove_whitespace_verticaly(board, a, b);	--b;	++flag;      }      else {	if (board[b+1][a] == EMPTY_TILE) {	  board[b+1][a] = board[b][a];	  board[b][a] = EMPTY_TILE;	  --b;	} else if (board[b][a] == board[b+1][a]) {	  board[b][a] += board[b+1][a];	  board[b+1][a] = EMPTY_TILE;	}      }    }  }} /* * ... print the board */ void print_board(int board[ROWS][COLUMNS]){  int a, b;   for (a = 0; a < ROWS; ++a) {    printf("\n");    for (b = 0; b < COLUMNS; ++b) {      printf("%5i", board[a][b]);    }  }  printf("\n");} /* * The reverse_board function reverses the array * if the movement is right or down reverse the array*/ void reverse_board(char input[], int board[ROWS][COLUMNS]){  int a, b, c, tmp;   if (!strcmp(input, "right")) {    for (a = 0; a < ROWS; ++a) {      for (b = 0, c = 3; b < 2; ++b, --c) {	tmp = board[a][b];	board[a][b] = board[a][c];	board[a][c] = tmp;      }    }  }  else if  (!strcmp(input, "down")) {    for (a = 0; a < COLUMNS; ++a) {      for (b = 0, c = 3; b < 2; ++b, --c) {	tmp = board[b][a];	board[b][a] = board[c][a];	board[c][a] = tmp;      }    }  }} /* * the check_board function is the one which evaluates the win or lose condition * for each turn and at the same time providing the number of empty tiles for the random generator * function*/ int check_board (int board[ROWS][COLUMNS]){  int a, b;   int result = 0;  int empty_tiles = 0;    for (a = 0; a < ROWS; ++a)    for (b = 0; b < COLUMNS; ++b)      if (board[a][b] == 2048)	result = -1;      else if (board[a][b] == EMPTY_TILE)	++empty_tiles;   result = result == -1 ? result : empty_tiles;   return result;} /* * the generate_random functin generates a random number between 0 and the number of * empty tiles. the generated number will assign to the Nth empty tile = (random_number)  * the new value, it also takes care of the 10% chance for producing a 4 tile*/ void generate_random(int board[ROWS][COLUMNS], int empty_tiles ){   srand(time(NULL));   int a, b;  int random = 0;  int tile = 0;   random = rand() % empty_tiles;  tile = (rand() % 9 == 4) ? 4 : 2;   for (a = 0; a < ROWS; ++a)    for (b = 0; b < COLUMNS; ++b)      if (board[a][b] == EMPTY_TILE && random != 0)	--random;      else if (board[a][b] == EMPTY_TILE && random == 0) {	board[a][b] = tile;	return;      }} /* * infinite loop, get the movements or exit code and act accordingly */ int play_game(int board[ROWS][COLUMNS]){   char movement[81];  int tiles = 0;   printf("this is the 2048 game\n"					\	 "The goal of this game is make a tile reach the value of 2048\n"\	 "The board starts of with only one occupied tile.\n"\	 "On each round a new tile gets added with the value of 2\n"\	 "or at 10%% of the times with the value of 4\n"\	 "If you run out of tiles you lose\n"\	 "There are 4 movements you can supply to the game\n"\	 "right, left, up, and down.\n"\	 "For each of this movements the tiles move to the direction specified\n"\	 "If two tiles have the same value the get added up just once.\n"\	 "If 2 occupied tiles share the same row or column but are seperated by empty tiles\n"\	 "then the occupied tiles travel along the empty tiles stacking in the direction\n"\	 "they were directed\n"\	 "For a more visual explanation you can check the wikipedia entry\n"	 " if you search for 2058 board game\n"	\	 "Here we go\n");   print_board(board);  while (1) {    printf("(enter: left,right,up,down,exit)>> ");    scanf("%s", movement);    if (!strcmp(movement, "down")) {      reverse_board(movement,board);      add_tiles_verticaly(board);      tiles = check_board(board);      if (tiles == -1)	return -1;      else if (tiles == 0)	return 0;      generate_random(board,tiles);      reverse_board(movement, board);    }    else if (!strcmp(movement, "up")) {      add_tiles_verticaly(board);      tiles = check_board(board);      if (tiles == -1)	return -1;      else if (tiles == 0)	return 0;      generate_random(board,tiles);    }    else if (!strcmp(movement, "right")) {      reverse_board(movement,board);      add_tiles_horizontaly(board);      tiles = check_board(board);      if (tiles == -1)	return -1;      else if (tiles == 0)	return 0;      generate_random(board,tiles);      reverse_board(movement, board);    }    else if (!strcmp(movement, "left")) {      add_tiles_horizontaly(board);      tiles = check_board(board);      if (tiles == -1)	return -1;      else if (tiles == 0)	return 0;      generate_random(board,tiles);    }    else if (!strcmp(movement, "exit")) {      return 1;    }    else {      printf("Do not recognize this movement please type again\n");      continue;    }    print_board(board);  }}  int main(void){  int play_game(int board[ROWS][COLUMNS]);  void generate_random(int board[ROWS][COLUMNS], int empty_tiles );  int check_board (int board[ROWS][COLUMNS]);  void reverse_board(char input[], int board[ROWS][COLUMNS]);  void print_board(int board[ROWS][COLUMNS]);  void add_tiles_verticaly(int board[ROWS][COLUMNS]);  void add_tiles_horizontaly(int board[ROWS][COLUMNS]);  void remove_whitespace_verticaly(int board[ROWS][COLUMNS], int columns, int rows);  void remove_whitespace_horizontaly(int board[ROWS][COLUMNS], int rows, int columns);   int win_condition;  int board[ROWS][COLUMNS] = {    {0,0,0,0},    {0,0,0,0},    {0,0,0,0},    {0,0,0,0}  };    generate_random(board, 16); /* initialize the board */   win_condition = play_game(board);  switch (win_condition) {  case 1:    printf("But you are not done yet!!!\n"	\	   "Fine, see you another day\n");    break;  case 0:    printf("Ohh noo, you run out of tiles\n"	\	   "Run me agan to play some more\n"	\	   "Byyyeee\n");    break;  case -1:    printf("WooooW you did it, Good job!!!\n"	\	   "See ya later homie\n");    break;  }   return 0;} `

## C#

Translation of: C++
`using System; namespace g2048_csharp{    internal class Tile    {        public Tile()        {            Value = 0;            IsBlocked = false;        }         public int Value { get; set; }        public bool IsBlocked { get; set; }    }     internal enum MoveDirection    {        Up,        Down,        Left,        Right    }     internal class G2048    {        public G2048()        {            _isDone = false;            _isWon = false;            _isMoved = true;            _score = 0;            InitializeBoard();        }         private void InitializeBoard()        {            for (int y = 0; y < 4; y++)            {                for (int x = 0; x < 4; x++)                {                    _board[x, y] = new Tile();                }            }        }         private bool _isDone;        private bool _isWon;        private bool _isMoved;        private int _score;        private readonly Tile[,] _board = new Tile[4, 4];        private readonly Random _rand = new Random();         public void Loop()        {            AddTile();            while (true)            {                if (_isMoved)                {                    AddTile();                }                 DrawBoard();                if (_isDone)                {                    break;                }                 WaitKey();            }             string endMessage = _isWon ? "You've made it!" : "Game Over!";            Console.WriteLine(endMessage);        }         private void DrawBoard()        {            Console.Clear();            Console.WriteLine("Score: " + _score + "\n");            for (int y = 0; y < 4; y++)            {                Console.WriteLine("+------+------+------+------+");                Console.Write("| ");                for (int x = 0; x < 4; x++)                {                    if (_board[x, y].Value == 0)                    {                        const string empty = " ";                        Console.Write(empty.PadRight(4));                    }                    else                    {                        Console.Write(_board[x, y].Value.ToString().PadRight(4));                    }                     Console.Write(" | ");                }                 Console.WriteLine();            }             Console.WriteLine("+------+------+------+------+\n\n");        }         private void WaitKey()        {            _isMoved = false;            Console.WriteLine("(W) Up (S) Down (A) Left (D) Right");            char input;            char.TryParse(Console.ReadKey().Key.ToString(), out input);             switch (input)            {                case 'W':                    Move(MoveDirection.Up);                    break;                case 'A':                    Move(MoveDirection.Left);                    break;                case 'S':                    Move(MoveDirection.Down);                    break;                case 'D':                    Move(MoveDirection.Right);                    break;            }             for (int y = 0; y < 4; y++)            {                for (int x = 0; x < 4; x++)                {                    _board[x, y].IsBlocked = false;                }            }        }         private void AddTile()        {            for (int y = 0; y < 4; y++)            {                for (int x = 0; x < 4; x++)                {                    if (_board[x, y].Value != 0) continue;                    int a, b;                    do                    {                        a = _rand.Next(0, 4);                        b = _rand.Next(0, 4);                    } while (_board[a, b].Value != 0);                     double r = _rand.NextDouble();                    _board[a, b].Value = r > 0.89f ? 4 : 2;                     if (CanMove())                    {                        return;                    }                }            }             _isDone = true;        }         private bool CanMove()        {            for (int y = 0; y < 4; y++)            {                for (int x = 0; x < 4; x++)                {                    if (_board[x, y].Value == 0)                    {                        return true;                    }                }            }             for (int y = 0; y < 4; y++)            {                for (int x = 0; x < 4; x++)                {                    if (TestAdd(x + 1, y, _board[x, y].Value)                        || TestAdd(x - 1, y, _board[x, y].Value)                        || TestAdd(x, y + 1, _board[x, y].Value)                        || TestAdd(x, y - 1, _board[x, y].Value))                    {                        return true;                    }                }            }             return false;        }         private bool TestAdd(int x, int y, int value)        {            if (x < 0 || x > 3 || y < 0 || y > 3)            {                return false;            }             return _board[x, y].Value == value;        }         private void MoveVertically(int x, int y, int d)        {            if (_board[x, y + d].Value != 0                && _board[x, y + d].Value == _board[x, y].Value                && !_board[x, y].IsBlocked                && !_board[x, y + d].IsBlocked)            {                _board[x, y].Value = 0;                _board[x, y + d].Value *= 2;                _score += _board[x, y + d].Value;                _board[x, y + d].IsBlocked = true;                _isMoved = true;            }            else if (_board[x, y + d].Value == 0                     && _board[x, y].Value != 0)            {                _board[x, y + d].Value = _board[x, y].Value;                _board[x, y].Value = 0;                _isMoved = true;            }             if (d > 0)            {                if (y + d < 3)                {                    MoveVertically(x, y + d, 1);                }            }            else            {                if (y + d > 0)                {                    MoveVertically(x, y + d, -1);                }            }        }         private void MoveHorizontally(int x, int y, int d)        {            if (_board[x + d, y].Value != 0                && _board[x + d, y].Value == _board[x, y].Value                && !_board[x + d, y].IsBlocked                && !_board[x, y].IsBlocked)            {                _board[x, y].Value = 0;                _board[x + d, y].Value *= 2;                _score += _board[x + d, y].Value;                _board[x + d, y].IsBlocked = true;                _isMoved = true;            }            else if (_board[x + d, y].Value == 0                     && _board[x, y].Value != 0)            {                _board[x + d, y].Value = _board[x, y].Value;                _board[x, y].Value = 0;                _isMoved = true;            }             if (d > 0)            {                if (x + d < 3)                {                    MoveHorizontally(x + d, y, 1);                }            }            else            {                if (x + d > 0)                {                    MoveHorizontally(x + d, y, -1);                }            }        }         private void Move(MoveDirection direction)        {            switch (direction)            {                case MoveDirection.Up:                    for (int x = 0; x < 4; x++)                    {                        int y = 1;                        while (y < 4)                        {                            if (_board[x, y].Value != 0)                            {                                MoveVertically(x, y, -1);                            }                             y++;                        }                    }                     break;                case MoveDirection.Down:                    for (int x = 0; x < 4; x++)                    {                        int y = 2;                        while (y >= 0)                        {                            if (_board[x, y].Value != 0)                            {                                MoveVertically(x, y, 1);                            }                             y--;                        }                    }                     break;                case MoveDirection.Left:                    for (int y = 0; y < 4; y++)                    {                        int x = 1;                        while (x < 4)                        {                            if (_board[x, y].Value != 0)                            {                                MoveHorizontally(x, y, -1);                            }                             x++;                        }                    }                     break;                case MoveDirection.Right:                    for (int y = 0; y < 4; y++)                    {                        int x = 2;                        while (x >= 0)                        {                            if (_board[x, y].Value != 0)                            {                                MoveHorizontally(x, y, 1);                            }                             x--;                        }                    }                     break;            }        }    }     internal static class Program    {        public static void Main(string[] args)        {            RunGame();        }         private static void RunGame()        {            G2048 game = new G2048();            game.Loop();             CheckRestart();        }         private static void CheckRestart()        {            Console.WriteLine("(N) New game (P) Exit");            while (true)            {                char input;                char.TryParse(Console.ReadKey().Key.ToString(), out input);                switch (input)                {                    case 'N':                        RunGame();                        break;                    case 'P':                        return;                    default:                        ClearLastLine();                        break;                }            }        }         private static void ClearLastLine()        {            Console.SetCursorPosition(0, Console.CursorTop);            Console.Write(new string(' ', Console.BufferWidth));            Console.SetCursorPosition(0, Console.CursorTop - 1);        }    }} `
Output:
```Score: 572

+------+------+------+------+
|      | 2    | 16   | 4    |
+------+------+------+------+
|      | 2    | 4    | 64   |
+------+------+------+------+
| 4    | 16   | 32   | 4    |
+------+------+------+------+
| 2    | 4    | 2    | 16   |
+------+------+------+------+

(W) Up (S) Down (A) Left (D) Right
```

## 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" );} `
Output:
```SCORE: 2024

+------+------+------+------+
|    2 |    8 |   32 |  256 |
+------+------+------+------+
|      |      |    4 |   32 |
+------+------+------+------+
|      |      |    2 |    8 |
+------+------+------+------+
|      |      |      |    2 |
+------+------+------+------+

(W)Up (S)Down (A)Left (D)Right
```

## Clojure

` (ns 2048  (:require [clojure.string :as str])) ;Preferences(def textures {:wall      "----+"               :cell      "%4s|"               :cell-edge "|"               :wall-edge "+"}) (def directions {:w :up                 :s :down                 :a :left                 :d :right}) (def field-size {:y 4 :x 4}) ;Output(defn cells->str [line]  (str (:cell-edge textures)       (str/join (map (partial format (:cell textures)) line))       "\n")) (defn walls->str [width]  (str (:wall-edge textures)       (str/join (repeat width (:wall textures)))       "\n")) (defn field->str [field]  (let [height (count field)        width (count (first field))]    (str (str/join (interleave (repeat height (walls->str width))                               (map cells->str field)))         (walls->str width)))) ;Misc(defn handle-input []  (let [input (read)        try-dir ((keyword input) directions)]    (if try-dir try-dir (recur)))) (defn get-columns [field]  (vec (for [x (range (count (first field)))]         (vec (for [y (range (count field))]                (get-in field [y x])))))) (defn reverse-lines [field]  (mapv #(vec (reverse %)) field)) (defn padding [coll n sym]  (vec (concat coll (repeat n sym)))) (defn find-empties [field]  (remove    nil?    (for [y (range (count field))          x (range (count (nth field y)))]      (when (= (get-in field [y x]) \space) [y x])))) (defn random-add [field]  (let [empties (vec (find-empties field))]    (assoc-in field              (rand-nth empties)              (rand-nth (conj (vec (repeat 9 2)) 4))))) (defn win-check [field]  (= 2048     (transduce       (filter number?)       (completing max)       0       (flatten field)))) (defn lose-check [field]  (empty? (filter (partial = \space) (flatten field)))) (defn create-start-field [y x]  (->> (vec (repeat y (vec (repeat x \space))))       (random-add)       (random-add))) ;Algo(defn lines-by-dir [back? direction field]  (case direction    :left field    :right (reverse-lines field)    :down (if back?            (get-columns (reverse-lines field))            (reverse-lines (get-columns field)))    :up (get-columns field))) (defn shift-line [line]  (let [len (count line)        line (vec (filter number? line))        max-idx (dec (count line))]    (loop [new [] idx 0]      (if (> idx max-idx)          (padding new (- len (count new)) \space)          (if (= (nth line idx) (get line (inc idx)))              (recur (conj new (* 2 (nth line idx))) (+ 2 idx))              (recur (conj new (nth line idx)) (inc idx))))))) (defn shift-field [direction field]  (->> (lines-by-dir false direction field)       (mapv shift-line)       (lines-by-dir true direction))) (defn handle-turn [field]  (let [direction (handle-input)]    (->> (shift-field direction field)         (random-add)))) (defn play-2048 []  (loop [field (create-start-field (:y field-size) (:x field-size))]    (println (field->str field))    (cond (win-check field) (println "You win")          (lose-check field) (println "You lose")          :default (recur (handle-turn field))))) (play-2048)`
Output:
```+----+----+----+----+
|    |   2|    |    |
+----+----+----+----+
|   2|    |    |    |
+----+----+----+----+
|   4|    |    |    |
+----+----+----+----+
|  16|   2|    |    |
+----+----+----+----+```

## Common Lisp

Depends on Windows msvcrt.dll for _getch. Depends on quicklisp. Use arrow keys to make moves and press "Q" to quit. Tested with SBCL.

`(ql:quickload '(cffi alexandria)) (defpackage :2048-lisp  (:use :common-lisp :cffi :alexandria)) (in-package :2048-lisp) (defvar *lib-loaded* nil) (unless *lib-loaded*  ;; Load msvcrt.dll to access _getch.  (define-foreign-library msvcrt    (:windows (:default "msvcrt")))   (use-foreign-library msvcrt)   (defcfun "_getch" :int)   (setf *lib-loaded* t)) (defun read-arrow ()  "Get an arrow key from input as UP, DOWN, LEFT, or RIGHT, otherwisereturn a char of whatever was pressed."  (let ((first-char (-getch)))    (if (= 224 first-char)        (case (-getch)          (75 'left)          (80 'down)          (77 'right)          (72 'up))        (code-char first-char)))) (defmacro swap (place1 place2)  "Exchange the values of two places."  (let ((temp (gensym "TEMP")))    `(cl:let ((,temp ,place1))       (cl:setf ,place1 ,place2)       (cl:setf ,place2 ,temp)))) (defun nflip (board &optional (left-right t))  "Flip the elements of a BOARD left and right or optionally up and down."  (let* ((y-len (array-dimension board 0))         (x-len (array-dimension board 1))         (y-max (if left-right y-len (truncate y-len 2)))         (x-max (if left-right (truncate x-len 2) x-len)))    (loop for y from 0 below y-max       for y-place = (- y-len 1 y) do         (loop for x from 0 below x-max            for x-place = (- x-len 1 x) do              (if left-right                  (swap (aref board y x) (aref board y x-place))                  (swap (aref board y x) (aref board y-place x)))))    board)) (defun flip (board &optional (left-right t))  "Flip the elements of a BOARD left and right or optionally up and down.Non-destructive version."  (nflip (copy-array board) left-right)) (defun transpose (board)  "Transpose the elements of BOARD into a new array."  (let* ((y-len (array-dimension board 0))         (x-len (array-dimension board 1))         (new-board (make-array (reverse (array-dimensions board)))))    (loop for y from 0 below y-len do         (loop for x from 0 below x-len do              (setf (aref new-board x y) (aref board y x))))    new-board)) (defun add-random-piece (board)  "Find a random empty spot on the BOARD to add a new piece.Return T if successful, NIL otherwise."  (loop     for x from 0 below (array-total-size board)     unless (row-major-aref board x)     count 1 into count     and collect x into indices     finally       (unless (= 0 count)         (setf (row-major-aref board (nth (random count) indices))               (if (= 0 (random 10)) 4 2))         (return t)))) (defun squash-line (line)  "Reduce a sequence of numbers from left to right according tothe rules of 2048. Return the score of squashing as well."  (let* ((squashed          (reduce           (lambda (acc x)             (if (equal x (car acc))                 (cons (list (* 2 x)) (cdr acc))                 (cons x acc)))           (nreverse (remove-if #'null line))           :initial-value nil))         (new-line (flatten squashed)))    (list (append (make-list (- (length line) (length new-line))) new-line)          (reduce #'+ (flatten (remove-if-not #'listp squashed)))))) (defun squash-board (board)  "Reduce each row of a board from left to right according tothe rules of 2048. Return the total score of squashing the board as well."  (let ((y-len (array-dimension board 0))        (x-len (array-dimension board 1))        (total 0))    (list (make-array (array-dimensions board) :initial-contents                      (loop for y from 0 below y-len                         for (line score) =                           (squash-line                            (make-array x-len                                        :displaced-to board                                        :displaced-index-offset                                        (array-row-major-index board y 0)))                         collect line                         do (incf total score)))          total))) (defun make-move (board direction)  "Make a move in the given DIRECTION on a new board."  ;; Move by always squashing right, but transforming the board as needed.  (destructuring-bind (new-board score)      (case direction        (up (squash-board (flip (transpose board))))        (down (squash-board (flip (transpose board) nil)))        (left (squash-board (nflip (flip board nil))))        (right (squash-board board)))    (let ((new-board           ;; Reverse the transformation.           (case direction             (up (transpose (nflip new-board)))             (down (transpose (nflip new-board nil)))             (left (nflip (nflip new-board nil)))             (right new-board))))      (unless (equalp board new-board)        (add-random-piece new-board)        (list new-board score))))) (defun winp (board winning-tile)  "Determine if a BOARD is in a winning state."  (loop for x from 0 below (array-total-size board)     for val = (row-major-aref board x)     when (eql val winning-tile) do (return t))) (defun game-overp (board)  "Determine if a BOARD is in a game over state."  ;; If a move is simulated in every direction and nothing changes,  ;; then we can assume there are no valid moves left.  (notany (lambda (dir) (car (make-move board dir)))          '(up down left right))) (defun print-divider (cells cell-size)  "A print helper function for PRINT-BOARD."  (dotimes (_ cells)    (princ "+")    (dotimes (_ cell-size)      (princ "-")))  (princ "+")  (terpri)) (defun print-board (board cell-size)  "Pretty print the given BOARD with a particular CELL-SIZE."  (let* ((y-len (array-dimension board 0))         (x-len (array-dimension board 1))         (super-size (+ 2 cell-size)))    (loop for y from 0 below y-len do         (print-divider x-len super-size)         (loop for x from 0 below x-len            for val = (aref board y x)            do              (princ "|")              (if val                  (format t " ~VD " cell-size val)                  (dotimes (_ super-size) (princ " "))))         (princ "|")         (terpri))    (print-divider x-len super-size))) (defun init-board ()  (let ((board (make-array '(4 4) :initial-element nil)))    (setf (row-major-aref board (random (array-total-size board))) 2)    board)) (defun prompt-input (board score &optional (check t))  (cond    ((and check (winp board 2048)) (format t "You win!"))    ((and check (game-overp board)) (format t "Game over..."))    (t (let ((choice (read-arrow)))         (cond           ((symbolp choice)            (destructuring-bind (&optional move (new-score 0))                (make-move board choice)              (if move                  (prompt move (+ score new-score))                  (prompt-input board score))))           ((find choice "qQ")            (format t "Quitting."))           (t (prompt-input board score nil))))))) (defun prompt (&optional (board (init-board)) (score 0))  (format t "~%   Score: ~D~%" score)  (print-board board 4)  (prompt-input board score))`
Output:
```* (2048-lisp::prompt)

Score: 0
+------+------+------+------+
|      |      |      |      |
+------+------+------+------+
|      |      |      |      |
+------+------+------+------+
|      |      |    2 |      |
+------+------+------+------+
|      |      |      |      |
+------+------+------+------+```

Some time later...

```   Score: 336
+------+------+------+------+
|      |    4 |   16 |   32 |
+------+------+------+------+
|      |      |    4 |   16 |
+------+------+------+------+
|      |      |   32 |      |
+------+------+------+------+
|      |    2 |      |      |
+------+------+------+------+```

## D

Translation of: C++
`import std.stdio, std.string, std.random;import core.stdc.stdlib: exit; struct G2048 {    public void gameLoop() /*@safe @nogc*/ {        addTile;        while (true) {            if (moved)                addTile;            drawBoard;            if (done)                break;            waitKey;        }        writeln(win ? "You win!" : "Game Over!");    } private:    static struct Tile {        uint val = 0;        bool blocked = false;    }     enum moveDir { up, down, left, right }    enum uint side = 4;     Tile[side][side] board;    bool win = false, done = false, moved = true;    uint score = 0;     void drawBoard() const /*@safe @nogc*/ {        writeln("SCORE: ", score, "\n");        foreach (immutable y; 0 .. side) {            write("+------+------+------+------+\n| ");            foreach (immutable x; 0 .. side) {                if (board[x][y].val)                    writef("%4d", board[x][y].val);                else                    writef("%4s", " ");                write(" | ");            }            writeln;        }        "+------+------+------+------+\n".writeln;    }     void waitKey() /*@safe*/ {        moved = false;        "(W)Up (S)Down (A)Left (D)Right (Q)Quit: ".write;        immutable c = readln.strip.toLower;         switch (c) {            case "w": move(moveDir.up);    break;            case "a": move(moveDir.left);  break;            case "s": move(moveDir.down);  break;            case "d": move(moveDir.right); break;            case "q": endGame;             break;            default:                       break;        }         foreach (immutable y; 0 .. side)            foreach (immutable x; 0 .. side)                board[x][y].blocked = false;    }     void endGame() const {        writeln("Game ended with score: ", score);        exit(0);    }     void addTile() /*nothrow*/ @safe /*@nogc*/ {        foreach (immutable y; 0 .. side) {            foreach (immutable x; 0 .. side) {                if (!board[x][y].val) {                    uint a, b;                    do {                        a = uniform(0, side);                        b = uniform(0, side);                    } while (board[a][b].val);                     board[a][b].val = (uniform01 > 0.89) ? side : 2;                    if (canMove)                        return;                }            }        }        done = true;    }     bool canMove() const pure nothrow @safe @nogc {        foreach (immutable y; 0 .. side)            foreach (immutable x; 0 .. side)                if (!board[x][y].val)                    return true;         foreach (immutable y; 0 .. side) {            foreach (immutable x; 0 .. side) {                if (testAdd(x + 1, y, board[x][y].val) ||                    testAdd(x - 1, y, board[x][y].val) ||                    testAdd(x, y + 1, board[x][y].val) ||                    testAdd(x, y - 1, board[x][y].val))                return true;            }        }        return false;    }     bool testAdd(in uint x, in uint y, in uint v) const pure nothrow @safe @nogc {        if (x > 3 || y > 3)            return false;        return board[x][y].val == v;    }     void moveVertically(in uint x, in uint y, in uint d) pure nothrow @safe @nogc {        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)                moveVertically(x, y + d,  1);        } else {            if (y + d > 0)                moveVertically(x, y + d, -1);        }    }     void moveHorizontally(in uint x, in uint y, in uint d) pure nothrow @safe @nogc {        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)                moveHorizontally(x + d, y,  1);        } else {            if (x + d > 0)                moveHorizontally(x + d, y, -1);        }    }     void move(in moveDir d) pure nothrow @safe @nogc {        final switch (d) with(moveDir) {            case up:                foreach (immutable x; 0 .. side)                    foreach (immutable y; 1 .. side)                        if (board[x][y].val)                            moveVertically(x, y, -1);                break;            case down:                foreach (immutable x; 0 .. side)                    foreach_reverse (immutable y; 0 .. 3)                        if (board[x][y].val)                            moveVertically(x, y, 1);                break;            case left:                foreach (immutable y; 0 .. side)                    foreach (immutable x; 1 .. side)                        if (board[x][y].val)                            moveHorizontally(x, y, -1);                break;            case right:                foreach (immutable y; 0 .. side)                    foreach_reverse (immutable x; 0 .. 3)                        if (board[x][y].val)                            moveHorizontally(x, y, 1);        }    }} void main() /*safe*/ {    G2048 g;    g.gameLoop;}`

The output is the same as the C++ version.

## Elixir

Works with: Elixir version 1.3
`defmodule Game2048 do  @size 4  @range [email protected]   def play(goal \\ 2048), do: setup() |> play(goal)   defp play(board, goal) do    show(board)    cond do      goal in Map.values(board) ->          IO.puts "You win!"          exit(:normal)      0 in Map.values(board) or combinable?(board) ->          moved = move(board, keyin())          if moved == board, do: play(board, goal), else: add_tile(moved) |> play(goal)      true ->          IO.puts "Game Over!"          exit(:normal)    end  end   defp setup do    (for i <- @range, j <- @range, into: %{}, do: {{i,j},0})    |> add_tile    |> add_tile  end   defp add_tile(board) do    position = blank_space(board) |> Enum.random    tile = if :rand.uniform(10)==1, do: 4, else: 2    %{board | position => tile}  end   defp blank_space(board) do    for {key, 0} <- board, do: key  end   defp keyin do    key = IO.gets("key in wasd or q: ")    case String.first(key) do      "w" -> :up      "a" -> :left      "s" -> :down      "d" -> :right      "q" -> exit(:normal)      _   -> keyin()    end  end   defp move(board, :up) do    Enum.reduce(@range, board, fn j,acc ->      Enum.map(@range, fn i -> acc[{i,j}] end)      |> move_and_combine      |> Enum.with_index      |> Enum.reduce(acc, fn {v,i},map -> Map.put(map, {i,j}, v) end)    end)  end  defp move(board, :down) do    Enum.reduce(@range, board, fn j,acc ->      Enum.map(@size-1..0, fn i -> acc[{i,j}] end)      |> move_and_combine      |> Enum.reverse      |> Enum.with_index      |> Enum.reduce(acc, fn {v,i},map -> Map.put(map, {i,j}, v) end)    end)  end  defp move(board, :left) do    Enum.reduce(@range, board, fn i,acc ->      Enum.map(@range, fn j -> acc[{i,j}] end)      |> move_and_combine      |> Enum.with_index      |> Enum.reduce(acc, fn {v,j},map -> Map.put(map, {i,j}, v) end)    end)  end  defp move(board, :right) do    Enum.reduce(@range, board, fn i,acc ->      Enum.map(@size-1..0, fn j -> acc[{i,j}] end)      |> move_and_combine      |> Enum.reverse      |> Enum.with_index      |> Enum.reduce(acc, fn {v,j},map -> Map.put(map, {i,j}, v) end)    end)  end   defp move_and_combine(tiles) do    (Enum.filter(tiles, &(&1>0)) ++ [0,0,0,0])    |> Enum.take(@size)    |> case do         [a,a,b,b] -> [a*2, b*2, 0, 0]         [a,a,b,c] -> [a*2, b, c, 0]         [a,b,b,c] -> [a, b*2, c, 0]         [a,b,c,c] -> [a, b, c*2, 0]         x         -> x       end  end   defp combinable?(board) do    Enum.any?(for i <- @range, j <- [email protected], do: board[{i,j}]==board[{i,j+1}]) or    Enum.any?(for j <- @range, i <- [email protected], do: board[{i,j}]==board[{i+1,j}])  end   @frame   String.duplicate("+----", @size) <> "+"  @format (String.duplicate("|~4w", @size) <> "|") |> to_charlist   # before 1.3 to_char_list   defp show(board) do    Enum.each(@range, fn i ->      IO.puts @frame      row = for j <- @range, do: board[{i,j}]      IO.puts (:io_lib.fwrite @format, row) |> to_string |> String.replace(" 0|", "  |")    end)    IO.puts @frame  endend Game2048.play 512`
Output:
```+----+----+----+----+
|    |   2|    |    |
+----+----+----+----+
|    |    |    |    |
+----+----+----+----+
|   2|    |    |    |
+----+----+----+----+
|    |    |    |    |
+----+----+----+----+
key in wasd or q: s
.
.
.
+----+----+----+----+
|   2|   4|   2|    |
+----+----+----+----+
|  16|    |    |    |
+----+----+----+----+
|   8|  16|  32|   2|
+----+----+----+----+
|  64| 256| 128|   4|
+----+----+----+----+
key in wasd or q: q
```

## Elm

Works with: Elm 0.18.0

Try online [1]

`module Main exposing (..) import Html exposing (Html, div, p, text, button, span, h2)import Html.Attributes exposing (class, style)import Html.Events exposing (onClick)import Keyboard exposing (KeyCode)import Randomimport Tuple  main =    Html.program        { init = ( { initialModel | waitingForRandom = True }, generateRandomTiles 2 )        , view = view        , update = update        , subscriptions = always (Keyboard.downs KeyPress)        }   -- MODEL  -- tiles either have a value (2, 4, 8, ...) or are emptytype alias Tile =    Maybe Int  type alias Model =    { score : Int    , tiles : List Tile    , hasLost : Bool    , winKeepPlaying : Bool    , waitingForRandom : Bool -- prevent user from giving input while waiting for Random Cmd to return    }  initialModel : ModelinitialModel =    { score = 0, tiles = List.repeat 16 Nothing, waitingForRandom = False, hasLost = False, winKeepPlaying = False}   -- UPDATE  type alias RandomTileInfo =    ( Int, Int )  type Msg    = KeyPress KeyCode    | AddRandomTiles (List RandomTileInfo)    | NewGame    | KeepPlaying   -- asks the random generator to generate the information required for later adding random tiles-- generate a random position for the and value (4 10%, 2 90%) for each tile -- this uses Random.pair and Random.list to get a variable number of such pairs with one CmdgenerateRandomTiles : Int -> Cmd MsggenerateRandomTiles num =    let        randomPosition =            Random.int 0 15         randomValue =            Random.int 1 10                |> Random.map                    (\rnd ->                        if rnd == 10 then                            4                        else                            2                    )         -- 10% chance        randomPositionAndValue =            Random.pair randomPosition randomValue    in        Random.list num randomPositionAndValue |> Random.generate AddRandomTiles   -- actually add a random tile to the modeladdRandomTile : RandomTileInfo -> List Tile -> List TileaddRandomTile ( newPosition, newValue ) tiles =    let        -- newPosition is a value between 0 and 15        -- go through the list and count the amount of empty tiles we've seen.        -- if we reached the newPosition % emptyTileCount'th empty tile, set its value to newValue        emptyTileCount =            List.filter ((==) Nothing) tiles |> List.length         -- if there are less than 16 empty tiles this is the number of empty tiles we pass        targetCount =            newPosition % emptyTileCount         set_ith_empty_tile tile ( countEmpty, newList ) =            case tile of                Just value ->                    ( countEmpty, (Just value) :: newList )                 Nothing ->                    if countEmpty == targetCount then                        -- replace this empty tile with the new value                        ( countEmpty + 1, (Just newValue) :: newList )                    else                        ( countEmpty + 1, Nothing :: newList )    in        List.foldr set_ith_empty_tile ( 0, [] ) tiles |> Tuple.second   -- core game mechanic: move numbers (to the left,-- moving to the right is equivalent to moving left on the reversed array)-- this function works on single columns/rowsmoveNumbers : List Tile -> ( List Tile, Int )moveNumbers tiles =    let        last =            List.head << List.reverse         -- init is to last what tail is to head        init =           List.reverse << List.drop 1 << List.reverse         doMove tile ( newTiles, addScore ) =            case tile of                -- omit empty tiles when shifting                Nothing ->                    ( newTiles, addScore )                 Just value ->                    case last newTiles of                        -- if the last already moved tile ...                        Just (Just value2) ->                            -- ... has the same value, add a tile with the summed value                            if value == value2 then                                ( (init newTiles) ++ [ Just (2 * value) ]                                , addScore + 2 * value )                            else                                -- ... else just add the tile                                ( newTiles ++ [ Just value ], addScore )                         _ ->                            -- ... else just add the tile                            ( newTiles ++ [ Just value ], addScore )         ( movedTiles, addScore ) =            List.foldl doMove ( [], 0 ) tiles    in        ( movedTiles ++ List.repeat (4 - List.length movedTiles) Nothing, addScore )  update : Msg -> Model -> ( Model, Cmd Msg )update msg model =    case msg of        -- new game button press        NewGame ->            if not model.waitingForRandom then                ( { initialModel | waitingForRandom = True }, generateRandomTiles 2 )            else                ( model, Cmd.none )         -- "keep playing" button on win screen        KeepPlaying ->            ( { model | winKeepPlaying = True }, Cmd.none)         -- Random generator Cmd response        AddRandomTiles tileInfos ->            let                newTiles =                    List.foldl addRandomTile model.tiles tileInfos            in                ( { model | tiles = newTiles, waitingForRandom = False }, Cmd.none )          KeyPress code ->            let                -- zip list and indices, apply filter, unzip                indexedFilter func list =                    List.map2 (,) (List.range 0 (List.length list - 1)) list                        |> List.filter func                        |> List.map Tuple.second                 -- the i'th row (of 4) contains elements i*4, i*4+1, i*4+2, i*4+3                -- so all elements for which index//4 == i                i_th_row list i =                    indexedFilter (((==) i) << (flip (//) 4) << Tuple.first) list                 -- the i'th col (of 4) contain elements i, i+4, i+2*4, i+3*4                -- so all elements for which index%4 == i                i_th_col list i =                    indexedFilter (((==) i) << (flip (%) 4) << Tuple.first) list                 -- rows and columns of the grid                rows list =                    List.map (i_th_row list) (List.range 0 3)                 cols list =                    List.map (i_th_col list) (List.range 0 3)                 -- move each row or column and unzip the results from each call to moveNumbers                move =                    List.unzip << List.map moveNumbers                 moveReverse =                    List.unzip << List.map (Tuple.mapFirst List.reverse << moveNumbers << List.reverse)                 -- concat rows back into a flat array and sum all addScores                unrows =                    Tuple.mapSecond List.sum << Tuple.mapFirst List.concat                 -- turn columns back into a flat array and sum all addScores                uncols =                    Tuple.mapSecond List.sum << Tuple.mapFirst (List.concat << cols << List.concat)                  -- when shifting left or right each row can be (reverse-) shifted separately                -- when shifting up or down each column can be (reveerse-) shifted separately                ( newTiles, addScore ) =                    case code of                        37 ->                            -- left                            unrows <| move <| rows model.tiles                         38 ->                            -- up                            uncols <| move <| cols model.tiles                         39 ->                            -- right                            unrows <| moveReverse <| rows model.tiles                         40 ->                            -- down                            uncols <| moveReverse <| cols model.tiles                         _ ->                            ( model.tiles, 0 )                  containsEmptyTiles =                    List.any ((==) Nothing)                 containsAnySameNeighbours : List Tile -> Bool                containsAnySameNeighbours list =                    let                        tail = List.drop 1 list                        init = List.reverse <| List.drop 1 <| List.reverse list                    in                        List.any (uncurry (==)) <| List.map2 (,) init tail                hasLost =                     -- grid full                    (not (containsEmptyTiles newTiles))                        -- and no left/right move possible                        && (not <| List.any containsAnySameNeighbours <| rows newTiles)                        -- and no up/down move possible                        && (not <| List.any containsAnySameNeighbours <| cols newTiles)                 ( cmd, waiting ) =                    if List.all identity <| List.map2 (==) model.tiles newTiles then                        ( Cmd.none, False )                    else                        ( generateRandomTiles 1, True )                 score =                    model.score + addScore            in                -- unsure whether this actually happens but regardless:                -- keep the program from accepting a new keyboard input when a new tile hasn't been spawned yet                if model.waitingForRandom then                    ( model, Cmd.none )                else                    ( { model | tiles = newTiles, waitingForRandom = waiting, score = score, hasLost = hasLost }, cmd )    -- VIEW  containerStyle : List ( String, String )containerStyle =    [ ( "width", "450px" )    , ( "height", "450px" )    , ( "background-color", "#bbada0" )    , ( "float", "left" )    , ( "border-radius", "6px")    ]  tileStyle : Int -> List ( String, String )tileStyle value =    let        color =            case value of                0 ->                    "#776e65"                 2 ->                    "#eee4da"                 4 ->                    "#ede0c8"                 8 ->                    "#f2b179"                 16 ->                    "#f59563"                 32 ->                    "#f67c5f"                 64 ->                    "#f65e3b"                 128 ->                    "#edcf72"                 256 ->                    "#edcc61"                 512 ->                    "#edc850"                 1024 ->                    "#edc53f"                 2048 ->                    "#edc22e"                 _ ->                    "#edc22e"    in        [ ( "width", "100px" )        , ( "height", "70px" )        , ( "background-color", color )        , ( "float", "left" )        , ( "margin-left", "10px" )        , ( "margin-top", "10px" )        , ( "padding-top", "30px" )        , ( "text-align", "center" )        , ( "font-size", "30px" )        , ( "font-weight", "bold" )        , ( "border-radius", "6px")        ]  viewTile : Tile -> Html MsgviewTile tile =    div [ style <| tileStyle <| Maybe.withDefault 0 tile ]        [ span [] [ text <| Maybe.withDefault "" <| Maybe.map toString tile ]        ]  viewGrid : List Tile -> Html MsgviewGrid tiles =    div [ style containerStyle ] <| List.map viewTile tiles  viewLost : Html MsgviewLost =    div        [ style containerStyle ]        [ div            [ style [ ( "text-align", "center" ) ] ]            [ h2 [] [ text "You lost!" ]            ]        ] viewWin : Html MsgviewWin =    div        [ style containerStyle ]        [ div            [ style [ ( "text-align", "center" ) ] ]            [ h2 [] [ text "Congratulations, You won!" ]            , button                [ style [ ( "margin-bottom", "16px" ), ( "margin-top", "16px" ) ], onClick KeepPlaying ]                [ text "Keep playing" ]            ]        ]  view : Model -> Html Msgview model =    div [ style [ ( "width", "450px" ) ] ]        [ p [ style [ ( "float", "left" ) ] ] [ text <| "Your Score: " ++ toString model.score ]        , button            [ style [ ( "margin-bottom", "16px" ), ( "margin-top", "16px" ), ( "float", "right" ) ], onClick NewGame ]            [ text "New Game" ]        , if model.hasLost then            viewLost          else if List.any ((==) (Just 2048)) model.tiles && not model.winKeepPlaying then            viewWin          else            viewGrid model.tiles        ] `

## Factor

Can be loaded and run as a module by copying the code to a file and executing "factor 2048.factor".

For every step prints an ASCII representation of the board on the console. Controlled by feeding the program lines with a single character:

• W - up
• S - down
• A - left
• D - right
• Q - exit the game

` USE: accessorsFROM: arrays => <array> array ;FROM: assocs => assoc-filter keys zip ;FROM: combinators => case cleave cond ;FROM: combinators.short-circuit => 1|| 1&& 2&& ;FROM: continuations => cleanup ;FROM: formatting => printf sprintf ;FROM: fry => '[ _ ;FROM: grouping => all-equal? clump group ;FROM: io => bl flush nl readln write ;FROM: kernel => = 2bi 2dup 2drop and bi bi* [email protected] boa boolean clone equal? dip drop dup if if* keep loop nip not over swap throw tri unless when with xor ;FROM: math => integer times * + > >= ;FROM: math.functions => ^ ;FROM: math.parser => hex> ;FROM: math.order => +lt+ +gt+ +eq+ ;FROM: random => random sample ;FROM: sequences => <iota> <repetition> any? all? append concat each first flip head if-empty interleave length map pop push reduce reverse second set-nth tail ;FROM: sorting => sort ;FROM: vectors => <vector> ;IN: 2048-game  ERROR: invalid-board ; SYMBOL: leftSYMBOL: rightSYMBOL: upSYMBOL: down TUPLE: tile{ level integer }; TUPLE: board{ width integer }{ height integer }{ tiles array }; M: tile equal?    {        [ and ] ! test for f        [ [ level>> ] [email protected] = ]    }    2&&; : valid-board? ( w h -- ? )    * 0 > ! board with 0 tiles does not have a meaningful representation; : <board> ( w h -- board )    [ valid-board? [ invalid-board throw ] unless ]    [ 2dup * f <array> board boa ] 2bi; : <tile> ( n -- tile )     tile boa; ! 1 in 10 tile starts as 4: new-tile ( -- tile )    10 random 0 = [ 2 ] [ 1 ] if    <tile>; <PRIVATE : space-left? ( board -- ? )    tiles>> [ f = ] any?; : rows>> ( board -- seq )    dup tiles>>    [ drop { } ] [ swap width>> group ] if-empty; : rows<< ( seq board -- )    [ concat ] dip tiles<<; : columns>> ( board -- seq )    rows>> flip; : columns<< ( seq board -- )    [ flip concat ] dip tiles<<; : change-rows ( board quote -- board )    over [ rows>> swap call( seq -- seq ) ] [ rows<< ] bi ; inline : change-columns ( board quote -- board )    over [ columns>> swap call( seq -- seq ) ] [ columns<< ] bi ; inline : can-move-left? ( seq -- ? )    {           ! one element seq cannot move        [ length 1 = not ]        ! empty seq cannot move        [ [ f = ] all? not ]         [ 2 clump             [                {                    ! test for identical adjescent tiles                    [ [ first ] [ second ] bi [ and ] [ = ] 2bi and ]                     ! test for empty space on the left and tile on the right                    [ [ first ] [ second ] bi [ xor ] [ drop f = ] 2bi and ]                } 1||            ] any?        ]    } 1&&; : can-move-direction? ( board direction -- ? )    {        { left  [ rows>>    [         can-move-left? ] any? ] }        { right [ rows>>    [ reverse can-move-left? ] any? ] }        { up    [ columns>> [         can-move-left? ] any? ] }        { down  [ columns>> [ reverse can-move-left? ] any? ] }     } case; : can-move-any? ( board -- ? )    { left right up down } [ can-move-direction? ] with any?; : empty-indices ( seq -- seq )    [ length <iota> ] keep zip    [ nip f = ] assoc-filter keys; : pick-random ( seq -- elem )    1 sample first; ! create a new tile on an empty space: add-tile ( board -- )    [ new-tile swap [ empty-indices pick-random ] keep [ set-nth ] keep ] change-tiles drop; ! combines equal tiles justified right or does nothing: combine-tiles ( tile1 tile2 -- tile3 tile4 )    2dup { [ and ] [ = ] } 2&&     [ drop [ 1 + ] change-level f swap ] when; : justify-left ( seq -- seq )    [           {            { [ dup  f = ] [ 2drop +lt+ ] }            { [ over f = ] [ 2drop +gt+ ] }            [ 2drop +eq+ ]        } cond    ] sort; : collapse ( seq -- seq )    justify-left     ! combine adjescent    dup length <vector>     [ over        [ swap [ push ] keep ]        [               {                [ pop combine-tiles ]                [ push ]                [ push ]             } cleave        ] if-empty    ] reduce     ! fill in the gaps after combination    justify-left; ! draws an objectGENERIC: draw ( obj -- ) PRIVATE> ! a single tile is higher than 2048 (level 10): won? ( board -- ? )     tiles>> [ dup [ level>> 11 >= ] when ] any? ; ! if there is no space left and no neightboring tiles are the same, end the board: lost? ( board -- ? )     {        [ space-left? ]        [ won? ]        [ can-move-any? ]     } 1|| not; : serialize ( board -- str )    [ width>> ]    [ height>> ]    [ tiles>>        [ dup f = [ drop 0 ] [ level>> ] if "%02x" sprintf ] map concat    ] tri    "%02x%02x%s" sprintf; : deserialize ( str -- board )    [ 2 head hex> ] [ 2 tail ] bi    [ 2 head hex> ] [ 2 tail ] bi    2 group [ hex> dup 0 = [ drop f ] [ tile boa ] if ] map    board boa; : move ( board direction -- )    {        { left  [ [ [         collapse         ] map ] change-rows    ] }        { right [ [ [ reverse collapse reverse ] map ] change-rows    ] }        { up    [ [ [         collapse         ] map ] change-columns ] }        { down  [ [ [ reverse collapse reverse ] map ] change-columns ] }    } case drop;  : get-input ( -- line )    readln; : parse-input ( line -- direction/f )    {        { "a" [ left  ] }        { "d" [ right ] }        { "w" [ up    ] }        { "s" [ down  ] }        { "q" [ f ] }        [ "Wrong input: %s\n" printf flush          get-input parse-input ]     } case; <PRIVATE : init ( board -- )    '[ _ add-tile ] 2 swap times;  M: tile draw ( tile -- )    level>> 2 swap ^ "% 4d" printf; M: boolean draw ( _ -- )    drop 4 [ bl ] times; : horizontal-line ( board -- )    width>>    " " write    "+------" <repetition> concat    write "+ " write nl; : separator ( -- )    " | " write; M: board draw ( board -- )    [ horizontal-line ] keep    [ rows>> ]    [        '[ _ horizontal-line ]        [   separator            [ separator ] [ draw ] interleave            separator nl        ] interleave    ]    [ horizontal-line ]    tri    flush; : update ( board -- f )    {        [             get-input parse-input [                {                    [ can-move-direction? ]                     [ over [ move ] [ add-tile ] bi* t ]                } 2&& drop t            ] [ drop f ] if*        ]        [ can-move-any? ]     } 1&&; : exit ( board -- )    {         { [ dup lost? ] [ "You lost! Better luck next time." write nl ] }        { [ dup won?  ] [ "You won! Congratulations!" write nl ] }        [ "Bye!" write nl ]    } cond drop; PRIVATE> : start-2048 ( -- )     4 4 <board>    [         ! Initialization        [ init ]        [ draw ]        ! Event loop        [ [ dup [ update ] [ draw ] bi ] loop ] tri    ]    ! Cleanup    [ exit ]    [ ]    cleanup; MAIN: start-2048 `

## Forth

Translation of: C
Works with: gforth version 0.7.3

Just like my implementation of 15 Puzzle Game, this uses Vim's h/j/k/l for movement.

`\ in Forth, you do many things on your own. This word is used to define 2D arrays: 2D-ARRAY ( height width )	CREATE DUP ,	* CELLS ALLOT	DOES> ( y x baseaddress )		ROT    ( x baseaddress y )		OVER @ ( x baseaddress y width )		*      ( x baseaddress y*width )		ROT    ( baseaddress y*width x )		+ 1+ CELLS +; require random.fsHERE SEED ! 0 CONSTANT D-INVALID1 CONSTANT D-UP2 CONSTANT D-DOWN3 CONSTANT D-LEFT4 CONSTANT D-RIGHT 4 CONSTANT NROWS4 CONSTANT NCOLS NROWS NCOLS * CONSTANT GRIDSIZENROWS NCOLS 2D-ARRAY GRIDCREATE HAVE-MOVED CELL ALLOTCREATE TOTAL-SCORE CELL ALLOTCREATE MOVE-SCORE CELL ALLOT : DIE-DIRECTIONCONST ." Unknown direction constant:" . BYE ;: ESC #ESC EMIT ;: CLS	ESC ." [2J"	ESC ." [H"; : GRID-VALUE 1 SWAP LSHIFT ;: DASHES 0 ?DO [CHAR] - EMIT LOOP ; : DRAW ( -- )	CLS ." Score: "	TOTAL-SCORE @ 0 U.R	MOVE-SCORE  @ ?DUP IF		."  (+" 0 U.R ." )"	THEN	CR 25 DASHES CR 	NROWS 0 ?DO		." |"		NCOLS 0 ?DO			J I GRID @ ?DUP IF				GRID-VALUE 4 U.R			ELSE				4 SPACES			THEN			."  |"		LOOP		CR	LOOP 	25 DASHES CR; : COUNT-FREE-SPACES ( -- free-spaces )	0 ( count )	NROWS 0 ?DO		NCOLS 0 ?DO			J I GRID @ 0= IF 1+ THEN		LOOP	LOOP; : GET-FREE-SPACE ( index -- addr )	0 0 GRID SWAP ( curr-addr index )	0 0 GRID @ 0<> IF 1+ THEN	0 ?DO ( find the next free space index times )		BEGIN			CELL+ DUP @ 0=		UNTIL	LOOP; : NEW-BLOCK ( -- )	COUNT-FREE-SPACES	DUP 0<= IF DROP EXIT THEN	RANDOM GET-FREE-SPACE	10 RANDOM 0= IF 2 ELSE 1 THEN SWAP !; : 2GRID ( a-y a-x b-y b-x -- a-addr b-addr ) GRID -ROT GRID SWAP ;: CAN-MERGE ( dest-addr other-addr -- can-merge? )	@ SWAP @ ( other-val dest-val )	DUP 0<> -ROT = AND; : CAN-GRAVITY ( dest-addr other-addr -- can-gravity? )	@ SWAP @ ( other-val dest-val )	0= SWAP 0<> AND; : TRY-MERGE ( dest-y dest-x other-y other-x -- )	2GRID ( dest-addr other-addr )	2DUP CAN-MERGE IF		TRUE HAVE-MOVED !		0 SWAP ! ( dest-addr )		DUP @ 1+ DUP ( dest-addr dest-val dest-val )		ROT ! ( dest-val )		GRID-VALUE DUP ( score-diff score-diff )		MOVE-SCORE +!		TOTAL-SCORE +!	ELSE		2DROP	THEN; : TRY-GRAVITY ( did-something-before operator dest-y dest-x other-y other-x -- did-something-after operator )	2GRID ( ... dest-addr other-addr )	2DUP CAN-GRAVITY IF		TRUE HAVE-MOVED !		DUP @ ( ... dest-addr other-addr other-val )		ROT ( ... other-addr other-val dest-addr ) ! ( ... other-addr )		0 SWAP !		NIP TRUE SWAP	ELSE		2DROP	THEN; : TRY-LOST? ( lost-before operator dy dx oy ox -- lost-after operator )	2GRID CAN-MERGE INVERT ( lost-before operator lost-now )	ROT AND SWAP ( lost-after operator ); : MOVEMENT-LOOP ( direction operator -- ) CASE	SWAP	D-UP OF NROWS 1- 0 ?DO		NCOLS 0 ?DO			J I J 1+ I 4 PICK EXECUTE		LOOP	LOOP ENDOF	D-DOWN OF 1 NROWS 1- ?DO		NCOLS 0 ?DO			J I J 1- I 4 PICK EXECUTE		LOOP	-1 +LOOP ENDOF	D-LEFT OF NCOLS 1- 0 ?DO		NROWS 0 ?DO			I J I J 1+ 4 PICK EXECUTE		LOOP	LOOP ENDOF	D-RIGHT OF 1 NCOLS 1- ?DO		NROWS 0 ?DO			I J I J 1- 4 PICK EXECUTE		LOOP	-1 +LOOP ENDOF	DIE-DIRECTIONCONSTENDCASE DROP ; : MERGE ( move -- ) ['] TRY-MERGE MOVEMENT-LOOP ;: GRAVITY-ONCE ( move -- success? ) FALSE SWAP ['] TRY-GRAVITY MOVEMENT-LOOP ;: GRAVITY ( move -- )	BEGIN		DUP GRAVITY-ONCE INVERT	UNTIL DROP; : MOVE-LOST? ( move -- lost? ) TRUE SWAP ['] TRY-LOST? MOVEMENT-LOOP ;: END-IF-LOST ( -- lost? )	COUNT-FREE-SPACES 0= IF		TRUE		5 1 DO I MOVE-LOST? AND LOOP		IF ." You lose!" CR BYE THEN	THEN; : END-IF-WON ( -- )	NROWS 0 ?DO		NCOLS 0 ?DO			J I GRID @ GRID-VALUE 2048 = IF ." You win!" CR BYE THEN		LOOP	LOOP; : TICK ( move -- )	FALSE HAVE-MOVED !	0 MOVE-SCORE !	DUP GRAVITY DUP MERGE GRAVITY	HAVE-MOVED @ IF NEW-BLOCK DRAW THEN	END-IF-WON	END-IF-LOST; : GET-MOVE ( -- move )	BEGIN		KEY CASE			#EOF OF BYE ENDOF			#ESC OF BYE ENDOF			[CHAR] q OF BYE ENDOF			[CHAR] Q OF BYE ENDOF			[CHAR] k OF D-UP TRUE ENDOF			[CHAR] K OF D-UP TRUE ENDOF			[CHAR] j OF D-DOWN TRUE ENDOF			[CHAR] J OF D-DOWN TRUE ENDOF			[CHAR] h OF D-LEFT TRUE ENDOF			[CHAR] H OF D-LEFT TRUE ENDOF			[CHAR] l OF D-RIGHT TRUE ENDOF			[CHAR] L OF D-RIGHT TRUE ENDOF			FALSE SWAP		ENDCASE	UNTIL; : INIT ( -- )	NROWS 0 ?DO		NCOLS 0 ?DO			0 J I GRID !		LOOP	LOOP 	0 TOTAL-SCORE !	0 MOVE-SCORE !	NEW-BLOCK	NEW-BLOCK	DRAW; : MAIN-LOOP ( -- )	BEGIN		GET-MOVE TICK	AGAIN; INITMAIN-LOOP`
Output:
```Score: 20136 (+2048)
-------------------------
|     |     |     |     |
|   2 |     |     |     |
|     |   8 |   2 |     |
|2048 |   4 |   8 |   2 |
-------------------------
You win!```

## Fortran

### The Plan

The primary objective was to achieve the processing without generating similar code for each of the four different move directions or alternatively for the two lots of related directions - left/right and up/down. The various directions each involve variations on a loop of the form `DO L = 1,N` and this can easily be generalised as `DO L = first,last,increment` with a set of suitable values for each direction. Although Fortran encompasses complex arithmetic so that one could play about with vector arithmetic (notably, multiplying by (0,1) rotates by ninety degrees counterclockwise), alas, this is not provided for integer type variables, and in any case, the (x,y) orientation of Cartesian coordinates is not the same as the (row,column) orientation usual for arrays and character-style output, so to reduce mental strain complex arithmetic is not attempted and screen layout rules. However, an echo remains in that the directions are listed in the (x,y) style counter-clockwise: right, up, left, down.

Further thought shows that a move in a selected direction also involves a direction at right angles. To reduce vague generality, suppose the move direction is "right". All squares in a row are to be shoved rightwards, and this is to be repeated for each row: a series perpendicular to the move direction. Indeed, since rows do not interact in this move each row could be processed in parallel, but an ordinary sequential loop will do. It could run in any order so only two sorts of directions need be handled, but to reduce the mental strain, all four are distinct. Thus, there is a two-level process: the outer loop steps through the collection of rows, and the inner loop deals with the movement in each row. The outer loop is controlled by arrays `RC1, RCn, RCi` for first, last, increment to step along the rows (or columns): RC. And for the inner loop perpendicular to that so CR for column (or row) there are arrays `CR1, CRn, CRi` - all this is intended to be read as `DO L = 1,N` but with extra verbiage because the loop might be `DO L = N,1,-1` instead.

Holding firmly to the one-dimensional aspect of the row's processing, the actual processing can be seen to be simple. For instance, step along an array comparing each element to its predecessor, as in A(I) and A(I - 1), or, (avoiding index arithmetic) maintain two indices: CI and PI for current and previous index. Start CI at element one, and run the loop as `DO L = 2,N` with on each iteration PI taking the value of CI, and CI being calculated afresh. Except that the loop has verbiage: DO L = (first + increment),last,increment.

But in fact, the board is represented as a two dimensional array. Fortran does not offer a special "index" type of variable so that if this was a two-element entity with the value (1,2), `A(this)` would be equivalent to `A(1,2)` One must write out the indices, as in `A(this(1),this(2))` On the other hand, F90 introduced array arithmetic and related statements, so one can declare CIJ to be a two-element array, and introduce array arithmetic similar to complex number arithmetic to juggle indices. Further, instead of using simple variables and IF-statements or the like to select amongst the directions, this is done by using array WAY, and its associate YAW to obtain a perpendicular direction. That is, for direction W, WAY(W) selects either (0,1) or (1,0) so that RC*WAY(W) switches the value of RC between the first or second dimension, and YAW is the other way around.

Except that WAY and YAW are two dimensional arrays (rather than a one-dimensional array of complex number pairs, alas) so that the expression is in fact `RC*WAY(W,1:2)` and the calculations for both indices are done together. Because Fortran uses the "column-major" ordering of elements in storage, successive elements of a multi-dimensional array have the leftmost index varying most rapidly so that the order is WAY(1,1), WAY(2,1), WAY(3,1), WAY(4,1), WAY(1,2), etc and statements such as DATA or PARAMETER whereby values can be listed employ that ordering. So that the list of values for WAY and YAW can be aligned in the source code with the similar lists for the arrays specifying the loop parameters for each direction, the ordering is WAY(4,2) rather than WAY(2,4) even though this means that the two values for a given direction are not in adjacent storage, unlike the two parts of a complex number.

Arrays WAY and YAW are reminiscent of "truth tables" in Boolean logic, and it is tempting to imagine that YAW = ¬WAY, but alas, a NOT operation applied to an integer variable will flip not just the lowest bit. Trying a .NOT. operation on LOGICAL variables instead will work as desired, except that their integer interpretations may not be as hoped for. Yet another ploy might be based on W being even/odd or odd/even, and similar trickery might be applied to the other arrays of constants, but, enough. The devious juggling of arrays is traditional in Fortran.

### Source

The initial attempt at showing the board relied rather heavily on FORMAT tricks, in particular the use of the <n> facility whereby the value of an integer expression can be inserted into a format statement's coding on-the-fly, as in the following.
`         WRITE (MSG,1)		!Roll forth a top/bottom boundary. No corner characters (etc.), damnit.    1   FORMAT ("|",<NC>(<W>("-"),"|"))	!Heavy reliance on runtime values in NC and W. But see FORMAT 22.    2     FORMAT ("|",<NC>(<W>(" "),"|"))	!No horizontal markings within a tile. See FORMAT 1.          WRITE (MSG,22) ((" ",L1  = 1,W),"|",C = 1,NC)	!Compare to FORMAT 2.   22     FORMAT ("|",666A1)				!A constant FORMAT, a tricky WRITE.    4     FORMAT ("|",<NC - 1>(<W>("-"),"+"),<W>("-"),"|")	!With internal + rather than |.`

This sort of thing is not necessarily accepted by all compilers, so instead the next stage was to convert to using complicated WRITE statements. If one regarded the various sizes (the values of NR, NC, W in the source) as truly fixed, literal constants could be used throughout. This would however mean that they would appear without explanation, and if one eventually attempted to recode with different values, mistakes would be likely. Thus below, FORMAT 3 has ` (<NC>(A1,I<W>),A1)` and if the <> scheme were unavailable, you'd have to use `(4(A1,I6),A1)` instead, not too troublesome a change. Or, the text of the format sequence could be written to a CHARACTER variable, as demonstrated in Multiplication_tables#Traditional_approach. Yet another approach might be `(666(A1,I6))` which relies on the addendum `A1` happening to be the same as the start of the `(A1,I6)` pair, but there is still the appearance of the literal constant six instead of <W>, and if there were to be any change to be made, it would have to be remembered...

`      SUBROUTINE SHOW(NR,NC,BOARD)	!Mess about.       INTEGER NR,NC		!Number of rows and columns.       INTEGER BOARD(NR,NC)	!The board. Actual storage is transposed!       INTEGER R,C 		!Steppers.       INTEGER L,L1		!Fingers.       INTEGER W		!A width.       PARAMETER (W = 6)	!Six will suffice for 2048, even 524288.       CHARACTER*(NC*(W + 1) + 1) ALINE       CHARACTER*1 TL,TR,BL,BR	!Corner characters: top left, etc. Code page 850, and 437?       CHARACTER*1 LR,RL,TD,BU	!Side joining: Left rightwards, right leftwards, top downwards, bottom upwards.       CHARACTER*1 VL,HL,XX	!Vertical and horizontal lines, line crossing.       PARAMETER (TL=CHAR(218),TR=CHAR(191),BL=CHAR(192),BR=CHAR(217))	!Works for the "code page" 437, and 850.       PARAMETER (LR=CHAR(195),RL=CHAR(180),TD=CHAR(194),BU=CHAR(193))	!Try the DOS command CHCP to see which is in use.       PARAMETER (VL=CHAR(179),HL=CHAR(196),XX=CHAR(197))		!Attempts to change the code page no longer work...       INTEGER MSG		!I/O unit number.       COMMON/IODEV/ MSG	!I talk to the trees...        WRITE (MSG,1) TL,((HL,L = 1,W),TD,C = 1,NC - 1),(HL,L = 1,W),TR	!Write the top edge, with downwards ticks.    1   FORMAT (666A1)		!Surely long enough.        DO R = 1,NR		!Chug down the rows.          WRITE (MSG,1) VL,((" ",L=1,W),VL,C = 1,NC - 1),(" ",L=1,W),VL	!Space vertically to make the tile look less rectangular.          WRITE (ALINE,3) (VL,BOARD(R,C),C = 1,NC),VL	!The columns of the row. Usage is BOARD(row,col) despite storage adjacency.    3     FORMAT (<NC>(A1,I<W>),A1)	!Fixed sizes might suffice.          DO C = 1,NC			!Now inspect each cell along the line.            L1  = 1 + (C - 1)*(W + 1) + 1	!Locate the first interior character.            IF (BOARD(R,C).LE.0) THEN		!Should this one be blank?              ALINE(L1 + W - 1:L1 + W - 1) = " "	!Yes. Scrub the lone zero at the end of the span.             ELSE				!Non blank, but, aligned right.              L = L1					!So, look for the first digit.              DO WHILE(ALINE(L:L).EQ." ")		!There is surely one digit to be found.                L = L + 1					!Not yet. Advance.              END DO					!End with L fingering the first digit.              IF (L.GT.L1) ALINE(L1 + (L - L1 + 1)/2:L1 + W - 1) =	!Halve (approx.) the spare space at the start.     &                     ALINE(L:L1 + W - 1)		!The first digit to the last digit.            END IF				!So much for that line segment.          END DO			!On to the next column.          WRITE (MSG,"(A)") ALINE	!Roll the fancy line, all in one go.          WRITE (MSG,1) VL,((" ",L=1,W),VL,C = 1,NC - 1),(" ",L=1,W),VL	!More vertical space.          IF (R.LT.NR) WRITE (MSG,1) LR,((HL,L = 1,W),XX,C = 1,NC - 1),	!Write an internal horizontal seam.     &                                   (HL,L = 1,W),RL		!Starting and ending with a horizontal tick.        END DO			!On to the next row.        WRITE (MSG,1) BL,((HL,L = 1,W),BU,C = 1,NC - 1),(HL,L = 1,W),BR	!Write the bottom edge, witrh upwards ticks.      END SUBROUTINE SHOW	!That was nice.       PROGRAM PUZZLE	!Some severe array juggling may indeed cause puzzlement.      INTEGER NR,NC,N			!Describes the shape of the board.      PARAMETER (NR = 4, NC = 4, N = NR*NC)	!Determines the shape of the board.      INTEGER BOARD(NR,NC)		!Thus transpose furrytran's column-major usage. Beware!!!      INTEGER BORED(N)			!This allows for consecutive comparisons.      EQUIVALENCE (BOARD,BORED)		!Because the arrays are in the same place.      INTEGER BIJ,PB,CB			!Juggles with the values of some  squares.      INTEGER STARTVALUE,STARTTILES,TARGET	!Document the starting value.      PARAMETER (TARGET = 2048,STARTVALUE = 2,STARTTILES = 2)	!Why not start with one?      INTEGER SCORE			!Count them all.      INTEGER I,IT,TRY			!Odds and ends.      INTEGER LIST(N)			!A list.      CHARACTER*1 WAYS(4),WAYC(4)	!In two dimensions, there are four possible ways to move.      CHARACTER*4 WAYI			!There is no equivalent of INDEX for searching arrays.      EQUIVALENCE (WAYS,WAYI)		!But this enables two interpretations of the same storage.      PARAMETER (WAYC = (/"R","U","L","D"/))	!These are the names for the available directions.      INTEGER W,M,RC,CR,CIJ(2),PIJ(2),WAY(4,2),YAW(4,2)	!Directions in array index terms.      INTEGER RC1(4),RCN(4),RCI(4), CR1(4),CRN(4),CRI(4)	!Loop control for the directions..      PARAMETER (RC1 = (/ 1, 1,NR,NC/), CR1 = (/ 1,NR,NC, 1/))	!Start values of the first and second loops.      PARAMETER (RCN = (/NR,NC, 1, 1/), CRN = (/NC, 1, 1,NR/))	!End values.      PARAMETER (RCI = (/+1,+1,-1,-1/), CRI = (/+1,-1,-1,+1/))	!Incrementing or decrementing accordingly.      PARAMETER (WAY = (/ 1, 0, 1, 0,            0, 1, 0, 1/))	!The first loop is either the row, or the column.      PARAMETER (YAW = (/ 0, 1, 0, 1,            1, 0, 1, 0/))	!The second loop is the other way around.      REAL VALUE			!Humph. Yet another interface to a "random" number generator.      CHARACTER*1 C		!A monocharacter response is anticipated.      INTEGER MSG,KBD		!I/O unit numbers.      COMMON/IODEV/ MSG,KBD	!Pass the word.       KBD = 5	!Standard input. (Keyboard -> Display screen)      MSG = 6	!Standard output. (Display screen)      WRITE (MSG,1) TARGET,NR,NC,STARTVALUE	!Announce.    1 FORMAT ("To play '",I0,"' with ",I0," rows and ",I0," columns.",/,     1"On each move, choose a direction (Up, Down, Left, Right)",/     2 "by typing the single letter U, D, L, R, or, a space to quit."/     3 "All squares will be pushed as far as possible that way.",/     4 "Those meeting with the same number will form one square",/     5 "with the sum of the numbers, and one becomes blank.",/     6 "After each move, a random blank square becomes ",I0,/)      WRITE (MSG,2)	!Now for some annoyance.    2 FORMAT ("An integer to start the 'random' number generator: ",\$)	!Not starting a new line.      READ (KBD,*) TRY	!Could use a time-of-day in microseconds, or similar.      CALL SEED(TRY)	!But this enables reproducibility. And cheating. Concoct a board layout.   10 BOARD = 0			!Clear for action.      DO I = 1,STARTTILES	!Place the initial tiles, with their starting values.   11   CALL RANDOM(VALUE)		!0 <= VALUE < 1.        IT = VALUE*N + 1		!1 <= IT <= N. Don't round up!        IF (BORED(IT).NE.0) GO TO 11	!Oops! Flounder towards another tile.        BORED(IT) = STARTVALUE		!The beginning.      END DO			!On to the next.      SCORE = STARTVALUE*STARTTILES	!Save some mental arithmetic.      TRY = 0		!No moves made yet. Consider possible moves. Think in (x,y) but convert those thimks to (row,column). Eurghf.   20 TRY = TRY + 1		!Here we go again.      CALL SHOW(NR,NC,BOARD)	!The current state.      WAYS = ""			!No moveable directions are known.      DO 21 W = 1,4		!One way or another, consider each possible direction.        DO RC = RC1(W),RCN(W),RCI(W)	!W = 1 = +x: consider each successive row.          CIJ = RC*WAY(W,1:2) + CR1(W)*YAW(W,1:2)	!Finger the first position.          DO CR = CR1(W) + CRI(W),CRN(W),CRI(W)		!W = 1; along the columns of the row.            PIJ = CIJ					!Retain the previous position.            CIJ = RC*WAY(W,1:2) + CR*YAW(W,1:2)		!Convert (RC,CR) to either (RC,CR) or (CR,RC).            BIJ = BOARD(CIJ(1),CIJ(2))			!Grab the current position's board state.            IF ((BOARD(PIJ(1),PIJ(2)).GT.0   .AND. BIJ.EQ.0)		!A non-empty tile to move to an empty one?     1      .OR.(BOARD(PIJ(1),PIJ(2)).EQ.BIJ .AND. BIJ.GT.0)) THEN	!Or, there is a pair, BOARD(CIJ) = BOARD(PIJ),              WAYS(W) = WAYC(W)					!Then this direction is available.              GO TO 21						!No need to seek further opportunities for its use.            END IF					!So much for the current position.          END DO				!Advance the scan along direction W.        END DO				!Advance to the next (row or column) at right angles to W.   21 CONTINUE			!Try another way. Cast forth an invitation, and obtain a choice.   30 WRITE (MSG,31) TRY,SCORE,WAYS	!Summary.   31 FORMAT ("Move",I4,", score ",I0,". Moves ",4A1,\$)	!The \$, of course, says "don't end the line".      IF (ALL(WAYS.EQ." ")) GO TO 600	!A gridlock?      WRITE (MSG,32)			!Nope. Invite a selection.   32 FORMAT (" ... Your move: ",\$)	!Awaits input, with a new line after pressing "enter".      IF (COUNT(WAYS.NE." ").EQ.1) THEN	!Or, perhaps it is a choice you can't refuse.        W = MAXLOC(ABS(ICHAR(WAYS) - ICHAR(" ")),DIM = 1)	!One element's value differes from " "...        WRITE (MSG,33) WAYS(W)			!Sieze control!   33   FORMAT (A1," is the only possibility!")	!Just don't ask for input.       ELSE				!But often, the human can decide.        READ (KBD,"(A)") C			!Just one character. The first one typed.        IF (C.LE." ") STOP "Oh well."		!Bored, already?        I = INDEX("ruld",C)			!A lowercase letter may be presented.        IF (I.GT.0) C = "RULD"(I:I)		!So, convert to uppercase, if worthy.        W = INDEX(WAYI,C)			!What is it? There is no search of elements of the array WAYS.        IF (W.LE.0) THEN			!Perhaps it is blocked.          WRITE (MSG,34) C				!Alas.   34     FORMAT ("Not a possible move! ",A)		!Just so.          GO TO 30					!Try again.        END IF					!So much for suspicion.      END IF				!A move has been chosen. Complete the selected move. Carefully avoid enabling cascades, so 1122 is pulled right to ..24, not .222 then ..42.   40 M = MOD(W + 1,4) + 1		!W is the direction of movement, its inverse, M, faces arrivals.      DO RC = RC1(M),RCN(M),RCI(M)	!Loop through the (rows/columns) at right angles to the selected anti-way.        PIJ = RC*WAY(M,1:2) + CR1(M)*YAW(M,1:2)	!Finger the first square, which may be empty.        PB = BOARD(PIJ(1),PIJ(2))		!Load it into my two-element buffer: PB and CB.        IF (PB.NE.0) BOARD(PIJ(1),PIJ(2)) = 0	!It may be returned to the board somewhere else.        DO CR = CR1(M) + CRI(M),CRN(M),CRI(M)	!Step along the (column/row) of the selected anti-direction.          CIJ = RC*WAY(M,1:2) + CR*YAW(M,1:2)		!Convert (RC,CR) to either CIJ = (RC,CR) or CIJ = (CR,RC).          CB = BOARD(CIJ(1),CIJ(2))			!Inspect this square.          IF (CB.EQ.0) CYCLE				!From nothing comes nothing.          BOARD(CIJ(1),CIJ(2)) = 0			!The board's value now lives precariously in CB.          IF (PB.EQ.0) THEN				!A waiting hole? (And, CB is not empty)            PB = CB						!Yes. Fill it. More may follow, after spaces.          ELSE						!Otherwise, two non-zero values are in hand.            IF (PB.EQ.CB) THEN					!If they match,              PB = PB + CB						!Combine the new with the old.              CB = 0							!The new one is gone.            END IF						!So much for matches.            BOARD(PIJ(1),PIJ(2)) = PB				!Roll the trailing value.            PIJ = PIJ + CRI(M)*YAW(M,1:2)			!Advance the finger.            PB = CB						!Shuffle along one.          END IF					!So much for that square.        END DO					!On to the next one along.        IF (PB.GT.0) BOARD(PIJ(1),PIJ(2)) = PB	!A tail end value?      END DO				!On to the next set. Choose a random blank square.   50 IT = 0		!None have been located. (There is surely one, as a move was possible)      DO I = 1,N	!Step through all the possible squares.        IF (BORED(I).LE.0) THEN	!Empty?          IT = IT + 1			!Yes. Augment my list.          LIST(IT) = I			!Recording available squares.        END IF			!So much for that square.      END DO		!On to the next.      IF (IT.GT.1) THEN	!If a choice s available,        CALL RANDOM(VALUE)	!Concoct another: 0 <= VALUE < 1.        IT = VALUE*IT + 1	!And thus with integer truncation, choose an empty square.      END IF		!So much for choices.      BORED(LIST(IT)) = STARTVALUE	!Fill the square.      SCORE = SCORE + STARTVALUE	!Might as well keep count.Check for success.   60 IF (ALL(BORED.LT.TARGET)) GO TO 20!Hi ho.      WRITE (MSG,61)			!A success message.   61 FORMAT (I0," has been reached!")	!No fancy colours nor flashing lights, nor even bells.      GO TO 20				!Carry on, anyway. Curses!  600 WRITE (MSG,601)		!Alas.  601 FORMAT ("None! Oh dear.")	!Nothing more can be done.      END	!That was fun. `

### Output

As usual, the aspect ratio of the display here differs from the "console"-type display on the computer monitor, so the square is rather oblong, and the vertical bars do not join. Rather to my surprise the special characters for the "corner" and crossing glyphs do display correctly. If the console display is copied to a text editor (UltraEdit in my case) they are translated to + signs for the crossing and corners! Further confusion is provided by any attempt to type in the character codes (ALT-218, etc.) as some (but not all) codes are translated by UltraEdit or the keyboard interface into other character codes. All-in-all, it is simpler to employ `CHAR(218)` in the source as plain text with no fiddling.

Input is a bit annoying, as Fortran doesn't offer an interface to the asynchronous keyboard routines (such as KeyPressed and ReadKey in Turbo Pascal, etc.) and the arrow keys are pre-empted for editing the input being typed, notably the up-arrow key recovers the text of the previous line typed. So, one must press an ordinary key and then signify the completion of your input by pressing the "enter" key. Other keys could be allowed, such as SWAZ or KIJM and the like (or UPEJ for a Dvorak keyboard) for "right", "up", "left" and "down", but you would still have to press the enter key as well.

```To play '2048' with 4 rows and 4 columns.
On each move, choose a direction (Up, Down, Left, Right)
by typing the single letter U, D, L, R, or, a space to quit.
All squares will be pushed as far as possible that way.
Those meeting with the same number will form one square
with the sum of the numbers, and one becomes blank.
After each move, a random blank square becomes 2

An integer to start the 'random' number generator: 12345
┌──────┬──────┬──────┬──────┐
│      │      │      │      │
│      │   2  │      │      │
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
│      │      │      │      │
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
│      │      │      │      │
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
│      │      │   2  │      │
│      │      │      │      │
└──────┴──────┴──────┴──────┘
Move   1, score 4. Moves RULD ... Your move: d
┌──────┬──────┬──────┬──────┐
│      │      │      │      │
│      │      │      │      │
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
│      │      │      │      │
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
│      │      │   2  │      │
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
│      │   2  │   2  │      │
│      │      │      │      │
└──────┴──────┴──────┴──────┘
Move   2, score 6. Moves RULD ... Your move:
```

## Go

`package main import (	"bufio"	"fmt"	"log"	"math/rand"	"os"	"os/exec"	"strconv"	"strings"	"text/template"	"time"	"unicode" 	"golang.org/x/crypto/ssh/terminal") const maxPoints = 2048const (	fieldSizeX = 4	fieldSizeY = 4)const tilesAtStart = 2const probFor2 = 0.9 type button int const (	_ button = iota	up	down	right	left	quit) var labels = func() map[button]rune {	m := make(map[button]rune, 4)	m[up] = 'W'	m[down] = 'S'	m[right] = 'D'	m[left] = 'A'	return m}()var keybinding = func() map[rune]button {	m := make(map[rune]button, 8)	for b, r := range labels {		m[r] = b		if unicode.IsUpper(r) {			r = unicode.ToLower(r)		} else {			r = unicode.ToUpper(r)		}		m[r] = b	}	m[0x03] = quit	return m}() var model = struct {	Score int	Field [fieldSizeY][fieldSizeX]int}{} var view = func() *template.Template {	maxWidth := 1	for i := maxPoints; i >= 10; i /= 10 {		maxWidth++	} 	w := maxWidth + 3	r := make([]byte, fieldSizeX*w+1)	for i := range r {		if i%w == 0 {			r[i] = '+'		} else {			r[i] = '-'		}	}	rawBorder := string(r) 	v, err := template.New("").Parse(`SCORE: {{.Score}}{{range .Field}}` + rawBorder + `|{{range .}} {{if .}}{{printf "%` + strconv.Itoa(maxWidth) + `d" .}}{{else}}` +		strings.Repeat(" ", maxWidth) + `{{end}} |{{end}}{{end}}` + rawBorder + ` (` + string(labels[up]) + `)Up (` +		string(labels[down]) + `)Down (` +		string(labels[left]) + `)Left (` +		string(labels[right]) + `)Right`)	check(err)	return v}() func check(err error) {	if err != nil {		log.Panicln(err)	}} func clear() {	c := exec.Command("clear")	c.Stdout = os.Stdout	check(c.Run())} func draw() {	clear()	check(view.Execute(os.Stdout, model))} func addRandTile() (full bool) {	free := make([]*int, 0, fieldSizeX*fieldSizeY) 	for x := 0; x < fieldSizeX; x++ {		for y := 0; y < fieldSizeY; y++ {			if model.Field[y][x] == 0 {				free = append(free, &model.Field[y][x])			}		}	} 	val := 4	if rand.Float64() < probFor2 {		val = 2	}	*free[rand.Intn(len(free))] = val 	return len(free) == 1} type point struct{ x, y int } func (p point) get() int      { return model.Field[p.y][p.x] }func (p point) set(n int)     { model.Field[p.y][p.x] = n }func (p point) inField() bool { return p.x >= 0 && p.y >= 0 && p.x < fieldSizeX && p.y < fieldSizeY }func (p *point) next(n point) { p.x += n.x; p.y += n.y } func controller(key rune) (gameOver bool) {	b := keybinding[key] 	if b == 0 {		return false	}	if b == quit {		return true	} 	var starts []point	var next point 	switch b {	case up:		next = point{0, 1}		starts = make([]point, fieldSizeX)		for x := 0; x < fieldSizeX; x++ {			starts[x] = point{x, 0}		}	case down:		next = point{0, -1}		starts = make([]point, fieldSizeX)		for x := 0; x < fieldSizeX; x++ {			starts[x] = point{x, fieldSizeY - 1}		}	case right:		next = point{-1, 0}		starts = make([]point, fieldSizeY)		for y := 0; y < fieldSizeY; y++ {			starts[y] = point{fieldSizeX - 1, y}		}	case left:		next = point{1, 0}		starts = make([]point, fieldSizeY)		for y := 0; y < fieldSizeY; y++ {			starts[y] = point{0, y}		}	} 	moved := false	winning := false 	for _, s := range starts {		n := s		move := func(set int) {			moved = true			s.set(set)			n.set(0)		}		for n.next(next); n.inField(); n.next(next) {			if s.get() != 0 {				if n.get() == s.get() {					score := s.get() * 2					model.Score += score					winning = score >= maxPoints 					move(score)					s.next(next)				} else if n.get() != 0 {					s.next(next)					if s.get() == 0 {						move(n.get())					}				}			} else if n.get() != 0 {				move(n.get())			}		}	} 	if !moved {		return false	} 	lost := false	if addRandTile() {		lost = true	Out:		for x := 0; x < fieldSizeX; x++ {			for y := 0; y < fieldSizeY; y++ {				if (y > 0 && model.Field[y][x] == model.Field[y-1][x]) ||					(x > 0 && model.Field[y][x] == model.Field[y][x-1]) {					lost = false					break Out				}			}		}	} 	draw() 	if winning {		fmt.Println("You win!")		return true	}	if lost {		fmt.Println("Game Over")		return true	} 	return false} func main() {	oldState, err := terminal.MakeRaw(0)	check(err)	defer terminal.Restore(0, oldState) 	rand.Seed(time.Now().Unix()) 	for i := tilesAtStart; i > 0; i-- {		addRandTile()	}	draw() 	stdin := bufio.NewReader(os.Stdin) 	readKey := func() rune {		r, _, err := stdin.ReadRune()		check(err)		return r	} 	for !controller(readKey()) {	}} `

`import System.IOimport Data.Listimport Data.Maybeimport Control.Monadimport Data.Randomimport Data.Random.Distribution.Categoricalimport System.Console.ANSIimport Control.Lens -- Logic -- probability to get a 4prob4 :: Doubleprob4 = 0.1 type Position = [[Int]] combine, shift :: [Int]->[Int]combine (x:y:l) | x==y = (2*x) : combine lcombine (x:l) = x : combine lcombine [] = [] shift l = take (length l) \$ combine (filter (>0) l) ++ [0,0..] reflect :: [[a]] ->[[a]]reflect = map reverse type Move = Position -> Position left, right, up, down :: Moveleft = map shiftright = reflect . left . reflectup = transpose . left . transposedown = transpose . right . transpose progress :: Eq a => (a -> a) -> a -> Maybe aprogress f pos = if pos==next_pos then Nothing else Just next_pos where next_pos= f pos lost, win:: Position -> Boollost pos = all isNothing [progress move pos| move<-[left,right,up,down] ] win = any \$ any (>=2048) go :: Position -> Maybe Move -> Maybe Positiongo pos move = move >>= flip progress pos  {--- Adding 2 or 4 without lens:update l i a = l1 ++ a : l2 where (l1,_:l2)=splitAt i lindicesOf l = [0..length l-1] add a x y pos = update pos y \$ update (pos !! y) x a add2or4 ::  Position -> RVar Positionadd2or4 pos = do  (x,y) <-  randomElement [(x,y) | y<-indicesOf pos, x<-indicesOf (pos!!y), pos!!y!!x ==0  ]  a <- categorical [(0.9::Double,2), (0.1,4) ]  return \$ add a x y pos-} -- or with lens:indicesOf :: [a] -> [ReifiedTraversal' [a] a]indicesOf l = [ Traversal \$ ix i | i <- [0..length l - 1] ] indices2Of :: [[a]] -> [ReifiedTraversal' [[a]] a]indices2Of ls = [ Traversal \$ i.j | Traversal i <- indicesOf ls, let Just l = ls ^? i, Traversal j <- indicesOf l] add2or4 ::  Position -> RVar Positionadd2or4 pos = do  xy <-  randomElement [ xy | Traversal xy <- indices2Of pos, pos ^? xy == Just 0 ]  a <- categorical [(1-prob4, 2), (prob4, 4) ]  return \$  pos & xy .~ a-- Easy, is'n it'? -- Main loopplay :: Position -> IO ()play pos = do   c <- getChar   case go pos \$ lookup c [('D',left),('C',right),('A',up),('B',down)] of      Nothing -> play pos      Just pos1 -> do         pos2 <- sample \$ add2or4 pos1         draw pos2         when (win pos2 && not (win pos)) \$ putStrLn \$ "You win! You may keep going."         if lost pos2 then putStrLn "You lost!"            else play pos2 main :: IO ()main = do  pos <- sample \$ add2or4 \$ replicate 4 (replicate 4 0)  draw pos  play pos -- Rendering-- See https://en.wikipedia.org/wiki/ANSI_escape_code#Colorscolors = [(0,"\ESC[38;5;234;48;5;250m     ") ,(2,"\ESC[38;5;234;48;5;255m  2  ") ,(4,"\ESC[38;5;234;48;5;230m  4  ") ,(8,"\ESC[38;5;15;48;5;208m  8  ") ,(16,"\ESC[38;5;15;48;5;209m  16 ") ,(32,"\ESC[38;5;15;48;5;203m  32 ") ,(64,"\ESC[38;5;15;48;5;9m  64 ") ,(128,"\ESC[38;5;15;48;5;228m 128 ") ,(256,"\ESC[38;5;15;48;5;227m 256 ") ,(512,"\ESC[38;5;15;48;5;226m 512 ") ,(1024,"\ESC[38;5;15;48;5;221m 1024") ,(2048,"\ESC[38;5;15;48;5;220m 2048") ,(4096,"\ESC[38;5;15;48;5;0m 4096") ,(8192,"\ESC[38;5;15;48;5;0m 8192") ,(16384,"\ESC[38;5;15;48;5;0m16384") ,(32768,"\ESC[38;5;15;48;5;0m32768") ,(65536,"\ESC[38;5;15;48;5;0m65536") ,(131072,"\ESC[38;5;15;48;5;90m131072") ] showTile x = fromJust (lookup x colors) ++ "\ESC[B\^H\^H\^H\^H\^H     \ESC[A\ESC[C" draw :: Position -> IO ()draw pos = do  setSGR [Reset]  clearScreen  hideCursor  hSetEcho stdin False  hSetBuffering stdin NoBuffering  setSGR [SetConsoleIntensity BoldIntensity]  putStr "\ESC[38;5;234;48;5;248m" -- set board color  setCursorPosition 0 0  replicateM_ 13 \$ putStrLn \$ replicate 26 ' '  setCursorPosition 1 1  putStrLn \$ intercalate "\n\n\n\ESC[C" \$ concatMap showTile `map` pos `

## J

Solution

`NB. 2048.ijs scriptNB. =========================================================NB. 2048 game engine require 'guid'([ 9!:1) _2 (3!:4) , guids 1            NB. randomly set initial random seed coclass 'g2048'Target=: 2048 new2048=: verb define  Gridsz=: 4 4  Points=: Score=: 0  Grid=: newnum^:2 ] Gridsz \$ 0) newnum=: verb define  num=. 2 4 {~ 0.1 > ?0   NB. 10% chance of 4  idx=. 4 \$. \$. 0 = y        NB. indicies of 0s  if. #idx do.               NB. handle full grid    idx=. ,/ ({~ 1 ? #) idx  NB. choose an index    num (<idx)} y  else. return. y  end.) mskmerge=: [: >/\.&.|. 2 =/\ ,&_1mergerow=: ((* >:) #~ _1 |. [email protected]]) mskmergescorerow=: +/@(+: #~ mskmerge) compress=: -.&0toLeft=: 1 :'4&{[email protected]([email protected])"1'toRight=: 1 : '_4&{[email protected]([email protected]&.|.)"1'toUp=: 1 : '(4&{[email protected]([email protected])"1)&.|:'toDown=: 1 : '(_4&{[email protected]([email protected]&.|.)"1)&.|:' move=: conjunction define  Points=: +/@, v Grid  update newnum^:(Grid [email protected]: ]) u Grid) noMoves=: (0 [email protected] ,)@(mergerow toRight , mergerow toLeft , mergerow toUp ,: mergerow toDown)hasWon=: Target e. , eval=: verb define  Score=: Score + Points  isend=. (noMoves , hasWon) y  msg=. isend # 'You lost!!';'You Won!!'  if. -. isend=. +./ isend do.    Points=: 0    msg=. 'Score is ',(": Score)  end.  isend;msg) showGrid=: echo NB. =========================================================NB. Console user interface g2048Con_z_=: conew&'g2048con' coclass 'g2048con'coinsert 'g2048' create=: verb define  echo Instructions  startnew y) destroy=: codestroyquit=: destroy startnew=: [email protected] left=: 3 :'mergerow toLeft move (scorerow toLeft)'right=: 3 :'mergerow toRight move (scorerow toRight)'up=: 3 :'mergerow toUp move (scorerow toUp)'down=: 3 :'mergerow toDown move (scorerow toDown)' update=: verb define  Grid=: y       NB. update global Grid  'isend msg'=. eval y  echo msg  showGrid y  if. isend do. destroy '' end.  empty'') Instructions=: noun define=== 2048 ===Object:   Create the number 2048 by merging numbers. How to play:  When 2 numbers the same touch, they merge.  - move numbers using the commands below:       right__grd ''       left__grd ''       up__grd ''       down__grd ''  - quit a game:       quit__grd ''  - start a new game:       grd=: g2048Con '')`

Usage

`   grd=: g2048Con '' Score is 00 0 0 20 2 0 00 0 0 00 0 0 0   right__grd ''Score is 00 0 0 20 0 0 20 0 0 00 0 0 2   down__grd ''Score is 40 0 0 00 0 0 00 0 4 40 0 0 2...`

## Java

Works with: Java version 8
`import java.awt.*;import java.awt.event.*;import java.util.Random;import javax.swing.*; public class Game2048 extends JPanel {     enum State {        start, won, running, over    }     final Color[] colorTable = {        new Color(0x701710), new Color(0xFFE4C3), new Color(0xfff4d3),        new Color(0xffdac3), new Color(0xe7b08e), new Color(0xe7bf8e),        new Color(0xffc4c3), new Color(0xE7948e), new Color(0xbe7e56),        new Color(0xbe5e56), new Color(0x9c3931), new Color(0x701710)};     final static int target = 2048;     static int highest;    static int score;     private Color gridColor = new Color(0xBBADA0);    private Color emptyColor = new Color(0xCDC1B4);    private Color startColor = new Color(0xFFEBCD);     private Random rand = new Random();     private Tile[][] tiles;    private int side = 4;    private State gamestate = State.start;    private boolean checkingAvailableMoves;     public Game2048() {        setPreferredSize(new Dimension(900, 700));        setBackground(new Color(0xFAF8EF));        setFont(new Font("SansSerif", Font.BOLD, 48));        setFocusable(true);         addMouseListener(new MouseAdapter() {            @Override            public void mousePressed(MouseEvent e) {                startGame();                repaint();            }        });         addKeyListener(new KeyAdapter() {            @Override            public void keyPressed(KeyEvent e) {                switch (e.getKeyCode()) {                    case KeyEvent.VK_UP:                        moveUp();                        break;                    case KeyEvent.VK_DOWN:                        moveDown();                        break;                    case KeyEvent.VK_LEFT:                        moveLeft();                        break;                    case KeyEvent.VK_RIGHT:                        moveRight();                        break;                }                repaint();            }        });    }     @Override    public void paintComponent(Graphics gg) {        super.paintComponent(gg);        Graphics2D g = (Graphics2D) gg;        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,                RenderingHints.VALUE_ANTIALIAS_ON);         drawGrid(g);    }     void startGame() {        if (gamestate != State.running) {            score = 0;            highest = 0;            gamestate = State.running;            tiles = new Tile[side][side];            addRandomTile();            addRandomTile();        }    }     void drawGrid(Graphics2D g) {        g.setColor(gridColor);        g.fillRoundRect(200, 100, 499, 499, 15, 15);         if (gamestate == State.running) {             for (int r = 0; r < side; r++) {                for (int c = 0; c < side; c++) {                    if (tiles[r][c] == null) {                        g.setColor(emptyColor);                        g.fillRoundRect(215 + c * 121, 115 + r * 121, 106, 106, 7, 7);                    } else {                        drawTile(g, r, c);                    }                }            }        } else {            g.setColor(startColor);            g.fillRoundRect(215, 115, 469, 469, 7, 7);             g.setColor(gridColor.darker());            g.setFont(new Font("SansSerif", Font.BOLD, 128));            g.drawString("2048", 310, 270);             g.setFont(new Font("SansSerif", Font.BOLD, 20));             if (gamestate == State.won) {                g.drawString("you made it!", 390, 350);             } else if (gamestate == State.over)                g.drawString("game over", 400, 350);             g.setColor(gridColor);            g.drawString("click to start a new game", 330, 470);            g.drawString("(use arrow keys to move tiles)", 310, 530);        }    }     void drawTile(Graphics2D g, int r, int c) {        int value = tiles[r][c].getValue();         g.setColor(colorTable[(int) (Math.log(value) / Math.log(2)) + 1]);        g.fillRoundRect(215 + c * 121, 115 + r * 121, 106, 106, 7, 7);        String s = String.valueOf(value);         g.setColor(value < 128 ? colorTable[0] : colorTable[1]);         FontMetrics fm = g.getFontMetrics();        int asc = fm.getAscent();        int dec = fm.getDescent();         int x = 215 + c * 121 + (106 - fm.stringWidth(s)) / 2;        int y = 115 + r * 121 + (asc + (106 - (asc + dec)) / 2);         g.drawString(s, x, y);    }      private void addRandomTile() {        int pos = rand.nextInt(side * side);        int row, col;        do {            pos = (pos + 1) % (side * side);            row = pos / side;            col = pos % side;        } while (tiles[row][col] != null);         int val = rand.nextInt(10) == 0 ? 4 : 2;        tiles[row][col] = new Tile(val);    }     private boolean move(int countDownFrom, int yIncr, int xIncr) {        boolean moved = false;         for (int i = 0; i < side * side; i++) {            int j = Math.abs(countDownFrom - i);             int r = j / side;            int c = j % side;             if (tiles[r][c] == null)                continue;             int nextR = r + yIncr;            int nextC = c + xIncr;             while (nextR >= 0 && nextR < side && nextC >= 0 && nextC < side) {                 Tile next = tiles[nextR][nextC];                Tile curr = tiles[r][c];                 if (next == null) {                     if (checkingAvailableMoves)                        return true;                     tiles[nextR][nextC] = curr;                    tiles[r][c] = null;                    r = nextR;                    c = nextC;                    nextR += yIncr;                    nextC += xIncr;                    moved = true;                 } else if (next.canMergeWith(curr)) {                     if (checkingAvailableMoves)                        return true;                     int value = next.mergeWith(curr);                    if (value > highest)                        highest = value;                    score += value;                    tiles[r][c] = null;                    moved = true;                    break;                } else                    break;            }        }         if (moved) {            if (highest < target) {                clearMerged();                addRandomTile();                if (!movesAvailable()) {                    gamestate = State.over;                }            } else if (highest == target)                gamestate = State.won;        }         return moved;    }     boolean moveUp() {        return move(0, -1, 0);    }     boolean moveDown() {        return move(side * side - 1, 1, 0);    }     boolean moveLeft() {        return move(0, 0, -1);    }     boolean moveRight() {        return move(side * side - 1, 0, 1);    }     void clearMerged() {        for (Tile[] row : tiles)            for (Tile tile : row)                if (tile != null)                    tile.setMerged(false);    }     boolean movesAvailable() {        checkingAvailableMoves = true;        boolean hasMoves = moveUp() || moveDown() || moveLeft() || moveRight();        checkingAvailableMoves = false;        return hasMoves;    }     public static void main(String[] args) {        SwingUtilities.invokeLater(() -> {            JFrame f = new JFrame();            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);            f.setTitle("2048");            f.setResizable(true);            f.add(new Game2048(), BorderLayout.CENTER);            f.pack();            f.setLocationRelativeTo(null);            f.setVisible(true);        });    }} class Tile {    private boolean merged;    private int value;     Tile(int val) {        value = val;    }     int getValue() {        return value;    }     void setMerged(boolean m) {        merged = m;    }     boolean canMergeWith(Tile other) {        return !merged && other != null && !other.merged && value == other.getValue();    }     int mergeWith(Tile other) {        if (canMergeWith(other)) {            value *= 2;            merged = true;            return value;        }        return -1;    }}`

## JavaScript

Uses the P5.js library.

` /* Tile object: */ function Tile(pos, val, puzzle){	this.pos     = pos;	this.val     = val;	this.puzzle  = puzzle;	this.merging = false; 	this.getCol = () => Math.round(this.pos % 4);	this.getRow = () => Math.floor(this.pos / 4); 	/* draw tile on a P5.js canvas: */ 	this.show = function(){		let padding = this.merging ? 0 : 5;		let size = 0.25*width;		noStroke();		colorMode(HSB, 255);		fill(10*(11 - Math.log2(this.val)), 50 + 15*Math.log2(this.val), 200);		rect(this.getCol()*size + padding, this.getRow()*size + padding, size - 2*padding, size - 2*padding);		fill(255);		textSize(0.1*width);		textAlign(CENTER, CENTER);		text(this.val, (this.getCol() + 0.5)*size, (this.getRow() + 0.5)*size);	} 	/* move tile in a given direction: */ 	this.move = function(dir){		let col = this.getCol() + (1 - 2*(dir < 0))*Math.abs(dir)%4;		let row = this.getRow() + (1 - 2*(dir < 0))*Math.floor(Math.abs(dir)/4);		let target = this.puzzle.getTile(this.pos + dir); 		if (col < 0 || col > 3 || row < 0 || row > 3) {			/* target position out of bounds */			return false;		} else if (target){			/* tile blocked by other tile */			if(this.merging || target.merging || target.val !== this.val)				return false; 			/* merge with target tile (equal values):*/ 			target.val += this.val;			target.merging = true;			this.puzzle.score += target.val;			this.puzzle.removeTile(this);			return true;		} 		/* move tile: */		this.pos += dir;		return true;	}} /* Puzzle object: */ function Puzzle(){	this.tiles    = [];	this.dir      = 0;	this.score    = 0;	this.hasMoved = false;	this.validPositions = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; 	this.getOpenPositions = () => this.validPositions.filter(i => this.tiles.map(x => x.pos).indexOf(i) === -1);	this.getTile          = pos => this.tiles.filter(x => x.pos === pos)[0];	this.removeTile       = tile => this.tiles.splice(this.tiles.indexOf(tile), 1);	this.winCondition     = () => this.tiles.some(x => x.val === 2048); 	/* check for valid moves: */ 	this.validMoves = function(){		/* return true if there are empty spaces */		if(this.tiles.length < 16)			return true; 		/* otherwise check for neighboring tiles with the same value */		let res = false;		this.tiles.sort((x,y) => x.pos - y.pos);		for(let i = 0; i < 16; i++)			res = res || ( (i%4 < 3) ? this.tiles[i].val === this.tiles[i+1].val : false )					  || ( (i  < 12) ? this.tiles[i].val === this.tiles[i+4].val : false );		return res;	} 	/* check win and lose condition: */ 	this.checkGameState = function(){		if(this.winCondition()){			alert('You win!');		} else if (!this.validMoves()){			alert('You Lose!');			this.restart();		}	} 	this.restart = function(){		this.tiles    = [];		this.dir      = 0;		this.score    = 0;		this.hasMoved = false;		this.generateTile();		this.generateTile();	} 	/* draw the board on the p5.js canvas: */ 	this.show = function(){		background(200);		fill(255);		textSize(0.05*width);		textAlign(CENTER, TOP);		text("SCORE: " + this.score, 0.5*width, width); 		for(let tile of this.tiles)			tile.show();	} 	/* update the board: */ 	this.animate = function(){		if(this.dir === 0)			return; 		/* move all tiles in a given direction */		let moving = false;		this.tiles.sort((x,y) => this.dir*(y.pos - x.pos));		for(let tile of this.tiles)			moving = moving || tile.move(this.dir); 		/* check if the move is finished and generate a new tile */		if(this.hasMoved && !moving){			this.dir = 0;			this.generateTile(); 			for(let tile of this.tiles)				tile.merging = false;		} 		this.hasMoved = moving;	} 	this.generateTile = function(){		let positions = this.getOpenPositions();		let pos       = positions[Math.floor(Math.random()*positions.length)];		let val       = 2 + 2*Math.floor(Math.random()*1.11);		this.tiles.push(new Tile(pos, val, this));	}	this.generateTile();	this.generateTile(); 	/* process key inputs: */ 	this.keyHandler = function(key){		if      (key === UP_ARROW)    this.dir = -4		else if (key === DOWN_ARROW)  this.dir = 4		else if (key === RIGHT_ARROW) this.dir = 1		else if (key === LEFT_ARROW)  this.dir = -1;	}}  let game; function setup() {	createCanvas(400, 420);		game = new Puzzle();} /* game loop: */ function draw() {	game.checkGameState();	game.animate();	game.show();} function keyPressed(){	game.keyHandler(keyCode);} `

## Julia

Uses the Gtk toolkit. Includes scoring, a choice of board size and toolbar buttons for Undo and New Game.

` using Gtk.ShortNames @enum Direction2048 Right Left Up Down """    shifttiles!The adding and condensing code is for a leftward shift, so if the move is notleftward, this will rotate matrix to make move leftward, move, then undo rotation."""function shifttiles!(b, siz, direction)    if direction == Right        tmpb = rot180(b); points, winner = leftshift!(tmpb, siz); tmpb = rot180(tmpb)    elseif direction == Up        tmpb = rotl90(b); points, winner = leftshift!(tmpb, siz); tmpb = rotr90(tmpb)    elseif direction == Down        tmpb = rotr90(b); points, winner = leftshift!(tmpb, siz); tmpb = rotl90(tmpb)    else # left movement function as coded        return leftshift!(b, siz)    end    for i in 1:siz, j in 1:siz        b[i,j] = tmpb[i,j]   # copy tmpb contents back to b (modifies b)    end    points, winnerend  function compactleft!(b, siz, row)    tmprow = zeros(Int, siz)    tmppos = 1    for j in 1:siz        if b[row,j] != 0            tmprow[tmppos] = b[row,j]            tmppos += 1        end    end    b[row,:] = tmprowend """    leftshift!Work row by row. First, compact tiles to the left if possible. Second, find andreplace paired tiles in the row, then re-compact. Keep score of merges and returnas pointsgained. If a 2048 value tile is created, return a winner true value."""function leftshift!(b, siz)    pointsgained = 0    winner = false    for i in 1:siz        compactleft!(b, siz, i)        tmprow = zeros(Int, siz)        tmppos = 1        for j in 1:siz-1            if b[i,j] == b[i,j+1]                b[i,j] = 2 * b[i,j]                b[i,j+1] = 0                pointsgained += b[i,j]                if b[i,j] == 2048     # made a 2048 tile, which wins game                    winner = true                end            end            if b[i,j] != 0                tmprow[tmppos] = b[i,j]                tmppos += 1            end        end        tmprow[siz] = b[i,siz]        b[i,:] = tmprow        compactleft!(b, siz, i)    end    pointsgained, winnerend """    app2048Run game app, with boardsize (choose 4 for original game) as an argument."""function app2048(bsize)    win = Window("2048 Game", 400, 400) |> (Frame() |> (box = Box(:v)))    toolbar = Toolbar()    newgame = ToolButton("New Game")    setproperty!(newgame, :label, "New Game")    setproperty!(newgame, :is_important, true)    undomove = ToolButton("Undo Move")    setproperty!(undomove, :label, "Undo Move")    setproperty!(undomove, :is_important, true)    map(w->push!(toolbar,w),[newgame,undomove])    grid = Grid()    map(w -> push!(box, w),[toolbar, grid])    buttons = Array{Gtk.GtkButtonLeaf,2}(bsize, bsize)    for i in 1:bsize, j in 1:bsize        grid[i,j] = buttons[i,j] = Button()        setproperty!(buttons[i,j], :expand, true)    end    board = zeros(Int, (bsize,bsize))    pastboardstates = []    score = 0    gameover = false    condition = Condition()    won = ""     function update!()        for i in 1:bsize, j in 1:bsize            label = (board[i,j] > 0) ? board[i,j]: " "            setproperty!(buttons[i,j], :label, label)        end        setproperty!(win, :title, "\$won 2048 Game  (Score: \$score)")    end    function newrandomtile!()        blanks = Array{Tuple{Int,Int},1}()        for i in 1:bsize, j in 1:bsize            if board[i,j] == 0                push!(blanks, (i,j))            end        end        if length(blanks) == 0            gameover = true        else            i,j = rand(blanks)            board[i,j] = (rand() > 0.8) ? 4 : 2        end    end    function initialize!(w)        won = ""        gameover = false        for i in 1:bsize, j in 1:bsize            board[i,j] = 0            setproperty!(buttons[i,j], :label, " ")        end        newrandomtile!()        update!()    end    function undo!(w)        if gameover == false            board = pop!(pastboardstates)            update!()        end    end    function keypress(w, event)        presses = Dict(37 => Up,    # code rotated 90 degrees                       38 => Left,  # because of Gtk coordinates                       39 => Down,  # y is downward positive                       40 => Right)        keycode = event.hardware_keycode        if haskey(presses, keycode) && gameover == false            push!(pastboardstates, copy(board))            newpoints, havewon = shifttiles!(board, bsize, presses[keycode])            score += newpoints            if havewon && won != "Winning"                won = "Winning"                info_dialog("You have won the game.")            end            newrandomtile!()            update!()            if gameover                info_dialog("Game over.\nScore: \$score")            end        end    end    endit(w) = notify(condition)    initialize!(win)    signal_connect(initialize!, newgame, :clicked)    signal_connect(undo!,undomove, :clicked)    signal_connect(endit, win, :destroy)    signal_connect(keypress, win, "key-press-event")        showall(win)    wait(condition)end  const boardsize = 4app2048(boardsize) `

## Kotlin

Stateless with focus on clarity rather than conciseness.

`import java.io.BufferedReaderimport java.io.InputStreamReader const val positiveGameOverMessage = "So sorry, but you won the game."const val negativeGameOverMessage = "So sorry, but you lost the game." fun main(args: Array<String>) {    val grid = arrayOf(            arrayOf(0, 0, 0, 0),            arrayOf(0, 0, 0, 0),            arrayOf(0, 0, 0, 0),            arrayOf(0, 0, 0, 0)    )     val gameOverMessage = run2048(grid)    println(gameOverMessage)} fun run2048(grid: Array<Array<Int>>): String {    if (isGridSolved(grid)) return positiveGameOverMessage    else if (isGridFull(grid)) return negativeGameOverMessage     val populatedGrid = spawnNumber(grid)    display(populatedGrid)     return run2048(manipulateGrid(populatedGrid, waitForValidInput()))} fun isGridSolved(grid: Array<Array<Int>>): Boolean = grid.any { row -> row.contains(2048) }fun isGridFull(grid: Array<Array<Int>>): Boolean = grid.all { row -> !row.contains(0) } fun spawnNumber(grid: Array<Array<Int>>): Array<Array<Int>> {    val coordinates = locateSpawnCoordinates(grid)    val number = generateNumber()     return updateGrid(grid, coordinates, number)} fun locateSpawnCoordinates(grid: Array<Array<Int>>): Pair<Int, Int> {    val emptyCells = arrayListOf<Pair<Int, Int>>()    grid.forEachIndexed { x, row ->        row.forEachIndexed { y, cell ->            if (cell == 0) emptyCells.add(Pair(x, y))        }    }     return emptyCells[(Math.random() * (emptyCells.size - 1)).toInt()]} fun generateNumber(): Int = if (Math.random() > 0.10) 2 else 4 fun updateGrid(grid: Array<Array<Int>>, at: Pair<Int, Int>, value: Int): Array<Array<Int>> {    val updatedGrid = grid.copyOf()    updatedGrid[at.first][at.second] = value    return updatedGrid} fun waitForValidInput(): String {    val input = waitForInput()    return if (isValidInput(input)) input else waitForValidInput()} fun isValidInput(input: String): Boolean = arrayOf("a", "s", "d", "w").contains(input) fun waitForInput(): String {    val reader = BufferedReader(InputStreamReader(System.`in`))    println("Direction?  ")    return reader.readLine()} fun manipulateGrid(grid: Array<Array<Int>>, input: String): Array<Array<Int>> = when (input) {    "a" -> shiftCellsLeft(grid)    "s" -> shiftCellsDown(grid)    "d" -> shiftCellsRight(grid)    "w" -> shiftCellsUp(grid)    else -> throw IllegalArgumentException("Expected one of [a, s, d, w]")} fun shiftCellsLeft(grid: Array<Array<Int>>): Array<Array<Int>> =        grid.map(::mergeAndOrganizeCells).toTypedArray() fun shiftCellsRight(grid: Array<Array<Int>>): Array<Array<Int>> =        grid.map { row -> mergeAndOrganizeCells(row.reversed().toTypedArray()).reversed().toTypedArray() }.toTypedArray() fun shiftCellsUp(grid: Array<Array<Int>>): Array<Array<Int>> {    val rows: Array<Array<Int>> = arrayOf(            arrayOf(grid[0][0], grid[1][0], grid[2][0], grid[3][0]),            arrayOf(grid[0][1], grid[1][1], grid[2][1], grid[3][1]),            arrayOf(grid[0][2], grid[1][2], grid[2][2], grid[3][2]),            arrayOf(grid[0][3], grid[1][3], grid[2][3], grid[3][3])    )     val updatedGrid = grid.copyOf()     rows.map(::mergeAndOrganizeCells).forEachIndexed { rowIdx, row ->        updatedGrid[0][rowIdx] = row[0]        updatedGrid[1][rowIdx] = row[1]        updatedGrid[2][rowIdx] = row[2]        updatedGrid[3][rowIdx] = row[3]    }     return updatedGrid} fun shiftCellsDown(grid: Array<Array<Int>>): Array<Array<Int>> {    val rows: Array<Array<Int>> = arrayOf(            arrayOf(grid[3][0], grid[2][0], grid[1][0], grid[0][0]),            arrayOf(grid[3][1], grid[2][1], grid[1][1], grid[0][1]),            arrayOf(grid[3][2], grid[2][2], grid[1][2], grid[0][2]),            arrayOf(grid[3][3], grid[2][3], grid[1][3], grid[0][3])    )     val updatedGrid = grid.copyOf()     rows.map(::mergeAndOrganizeCells).forEachIndexed { rowIdx, row ->        updatedGrid[3][rowIdx] = row[0]        updatedGrid[2][rowIdx] = row[1]        updatedGrid[1][rowIdx] = row[2]        updatedGrid[0][rowIdx] = row[3]    }     return updatedGrid} fun mergeAndOrganizeCells(row: Array<Int>): Array<Int> = organize(merge(row.copyOf())) fun merge(row: Array<Int>, idxToMatch: Int = 0, idxToCompare: Int = 1): Array<Int> {    if (idxToMatch >= row.size) return row    if (idxToCompare >= row.size) return merge(row, idxToMatch + 1, idxToMatch + 2)    if (row[idxToMatch] == 0) return merge(row, idxToMatch + 1, idxToMatch + 2)     return if (row[idxToMatch] == row[idxToCompare]) {        row[idxToMatch] *= 2        row[idxToCompare] = 0        merge(row, idxToMatch + 1, idxToMatch + 2)    } else {        if (row[idxToCompare] != 0) merge(row, idxToMatch + 1, idxToMatch + 2)        else merge(row, idxToMatch, idxToCompare + 1)    }} fun organize(row: Array<Int>, idxToMatch: Int = 0, idxToCompare: Int = 1): Array<Int> {    if (idxToMatch >= row.size) return row    if (idxToCompare >= row.size) return organize(row, idxToMatch + 1, idxToMatch + 2)    if (row[idxToMatch] != 0) return organize(row, idxToMatch + 1, idxToMatch + 2)     return if (row[idxToCompare] != 0) {        row[idxToMatch] = row[idxToCompare]        row[idxToCompare] = 0        organize(row, idxToMatch + 1, idxToMatch + 2)    } else {        organize(row, idxToMatch, idxToCompare + 1)    }} fun display(grid: Array<Array<Int>>) {    val prettyPrintableGrid = grid.map { row ->        row.map { cell ->            if (cell == 0) "    " else java.lang.String.format("%4d", cell)        }    }     println("New Grid:")    prettyPrintableGrid.forEach { row ->        println("+----+----+----+----+")        row.forEach { print("|\$it") }        println("|")    }    println("+----+----+----+----+")}`

Sample output:

```New Grid:
+----+----+----+----+
|   2|    |    |    |
+----+----+----+----+
|    |    |    |   2|
+----+----+----+----+
|   4|  16|    |    |
+----+----+----+----+
|  16|   4|   2|    |
+----+----+----+----+
Direction?
```

## M2000 Interpreter

` Module Game2048 {      \\ 10% 4 and 90% 2      Def GetTlleNumber()=If(Random(10)<2->4, 2)      \\ tile      Def Tile\$(x)=If\$(x=0->"[    ]", format\$("[{0::-4}]", x))      \\ empty board      BoardTileRight=lambda (x, y)->x+y*4      BoardTileLeft=lambda (x, y)->3-x+y*4      BoardTileUp=lambda (x, y)->x*4+y      BoardTileDown=lambda (x, y)->(3-x)*4+y      Dim Board(0 to 15)      Inventory EmptyTiles      \\ Score is a statement but we can use it as a variable too.      Score=0      \\ Win is also a statement but we can use it as a variable too.      Win=False      ExitNow=False      BoardDirection=BoardtileRight      Process(BoardDirection)      \\ Split Rem lines to insert start condition to check valid moves      Rem : board(0)=2      Rem : board(1)=2, 2, 2   ' place to (1), (2), (3)            While len(EmptyTiles) {            NewTile()            DrawBoard()            Action=False            do {                  a\$=key\$                  if len(a\$)=2 then {                        Action=true                                    Select case Asc(mid\$(a\$,2))                        Case 72                        BoardDirection=BoardTileUp                        Case 75                        BoardDirection=BoardTileRight                        Case 77                        BoardDirection=BoardTileLeft                        Case 80                        BoardDirection=BoardTileDown                        Case 79 ' End key                              ExitNow=True                        Else                        Action=false                        end select                       }            } until Action            If ExitNow then exit            Process(BoardDirection)      }      If Win then {            Print "You Win"      } Else {            Print "You Loose"      }      End      Sub Process(Boardtile)      Inventory EmptyTiles   ' clear inventory      local where, i, j, k      For i=0 to 3            Gravity()            k=boardtile(0,i)            For j=1 to 3                  where=boardtile(j,i)                  if Board(where)<>0 then {                        if board(k)=board(where) then {                               board(k)*=2 : score+=board(where): board(where)=0                               if board(k)=2048 Then Win=True : ExitNow=true                        }                  }                  k=where             Next j            Gravity()            For j=0 to 3                  where=boardtile(j,i)                  if board(where)=0 then Append EmptyTiles, where            Next j      Next i      End Sub      Sub NewTile()            local m=EmptyTiles(Random(0, len(EmptyTiles)-1)!)            Board(m)=GetTlleNumber()            Delete EmptyTiles, m      End Sub      Sub DrawBoard()            Refresh 2000            Cls            Cursor 0, 10            Local Doc\$, line\$            Document Doc\$            Doc\$=Format\$("Game 2048 Score {0}", score)            \\ Using Report 2 we use rendering as text, with center justify            Report 2, Doc\$            Doc\$={            }            Local i, j            For i=0 to 3                  line\$=""                  For j=0 to 3                        line\$+=Tile\$(Board(BoardTileRight(j, i)))                  Next j                  Print Over \$(2), Line\$                  Print                  Doc\$=Line\$+{                  }            Next i            Report 2, "Next:Use Arrows | Exit: Press End"            Refresh            ClipBoard Doc\$      End Sub           Sub Gravity()            k=-1            for j=0 to 3 {                  where=boardtile(j,i)                  if k=-1 then if board(where)=0 then k=j : continue                  if board(where)=0  then continue                  if k=-1 then continue                  board(boardtile(k,i))=board(where)                  board(where)=0                  k++            }        End Sub}Game2048 `

Each move copied to clipboard

Output:
```Game 2048 Score 14
[   2][   4][   8][   8]
[    ][   2][   2][   2]
[    ][    ][    ][    ]
[    ][    ][    ][    ]
Game 2048 Score 24
[    ][   2][   4][  16]
[    ][    ][   2][   4]
[    ][    ][    ][    ]
[    ][    ][   2][    ]
Game 2048 Score 26
[    ][    ][    ][    ]
[    ][    ][    ][    ]
[   2][    ][   4][  16]
[    ][   2][   4][   4]
Game 2048 Score 30
[    ][    ][    ][    ]
[    ][    ][    ][    ]
[    ][   2][    ][  16]
[   2][   2][   8][   4]

```

## Maple

This application requires a bunch of different components to properly run when being as close to the mobile game as possible. These components are: A math container for the grid, an arrow key for each direction, a restart button, a text box to display the game over/highscore/arrow key to start messages, labels for score and highscore, and textboxes for the highscore and score values. Once these are created, change the names to the ones in the main body of code, and include the proper procedures for the 4 arrows and the restart button.

Next is the main body of code:

` macro(SP=DocumentTools:-SetProperty, GP=DocumentTools:-GetProperty);G := module() 	export reset,f,getname;	local a:=Matrix(4):	local buttonpress:="False";	local score:=0;	local highscoreM,highscore,hscore,hname,M,j,k,z,e,move,r,c,q,w,checklose,loss,matrixtotextarea; 	getname:=proc();			hname:=GP("Name",value);		buttonpress:="True";		if score>hscore then			M:=Matrix(1, 2, [[score, hname]]):			ExportMatrix("this:///Score.csv",M);			reset();		else			reset();		end if;	end proc; 	matrixtotextarea:=proc(m)		local m2,colors;		colors:=["White","Beige","LightGrey",ColorTools:-Color("RGB", [255/255, 127/255,  80/255]),ColorTools:-Color("RGB", [255/255, 99/255,  71/255]),ColorTools:-Color("RGB", [255/255, 69/255,  0/255]),ColorTools:-Color("RGB", [255/255, 0/255,  0/255]),ColorTools:-Color("RGB", [255/255, 215/255,  0/255]), ColorTools:-Color("RGB", [255/255, 255/255,  0/255]),ColorTools:-Color("RGB", [204/255, 204/255,  0/255]),ColorTools:-Color("RGB", [153/255, 153/255,  0/255]),ColorTools:-Color("RGB", [102/255, 102/255,  0/255]), ColorTools:-Color("RGB", [0/255, 0/255,  0/255])]; 		m2 := ArrayTools:-Reshape(m^%T, [16,1]):		SP(seq([cat("TextArea",i),value,m2[i+1,1]],i=0..15));		SP(seq(["Table1",fillcolor[(`if`(i+1<5,1,`if`(i+1<9 and i+1>4,2,`if`(i+1<13 and i+1>8,3, `if`(i+1<17 and i+1>12,4,1))))),(i mod 4)+1],`if`(m2[i+1,1]=0,colors[1],`if`(m2[i+1,1]=2,colors[2],`if`(m2[i+1,1]=4,colors[3],`if`(m2[i+1,1]=8,colors[4],`if`(m2[i+1,1]=16,colors[5],`if`(m2[i+1,1]=32,colors[6],`if`(m2[i+1,1]=64,colors[7],`if`(m2[i+1,1]=128,colors[8],`if`(m2[i+1,1]=256,colors[9],`if`(m2[i+1,1]=512,colors[10],`if`(m2[i+1,1]=1024,colors[11],`if`(m2[i+1,1]=2048,colors[12],`if`(m2[i+1,1]>2048,colors[13],"White")))))))))))))],i=0..15));		SP(seq([cat("TextArea",i),fillcolor,`if`(m2[i+1,1]=0,colors[1],`if`(m2[i+1,1]=2,colors[2],`if`(m2[i+1,1]=4,colors[3],`if`(m2[i+1,1]=8,colors[4],`if`(m2[i+1,1]=16,colors[5],`if`(m2[i+1,1]=32,colors[6],`if`(m2[i+1,1]=64,colors[7],`if`(m2[i+1,1]=128,colors[8],`if`(m2[i+1,1]=256,colors[9],`if`(m2[i+1,1]=512,colors[10],`if`(m2[i+1,1]=1024,colors[11],`if`(m2[i+1,1]=2048,colors[12],`if`(m2[i+1,1]>2048,colors[13],"White")))))))))))))],i=0..15),refresh);		SP(seq([cat("TextArea",i),fontcolor,`if`(m2[i+1,1]=0,colors[1],`if`(m2[i+1,1]=2,colors[13],`if`(m2[i+1,1]=4,colors[13],"White")))],i=0..15),refresh);	end proc: 	reset:=proc();		highscoreM := Import("this:///Score.csv", output = Matrix);		hscore := highscoreM[1,1];		hname := highscoreM[1,2];		highscore:=sprintf("%s",cat(hscore,"\n",hname));		buttonpress:="False";		a:=Matrix(4, 4, [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]):		score:=0;		matrixtotextarea(a);		SP("Score/Lose",visible,true);		SP("Score/Lose",enabled,true);		SP("Score/Lose",caption,"Click an Arrow to begin.");		SP("Score",value,score);		SP("Highscore",value,highscore);		SP(seq([j, enabled, false], j in ["Name","Enter"])); 		SP(seq([j, visible, false], j in ["Name","Enter"])); 		SP(seq([j, enabled, true], j in ["Score","Highscore", seq(cat("Button",k),k=0..4)]));			SP(seq([j, visible, true], j in ["Score","Highscore", seq(cat("Button",k),k=0..4)]));	end proc; 	checklose:=proc();		for q from 2 to 4 do			for w from 4 to 1 by -1  do				if a[q,w]=a[q-1,w] then					loss:="False";					return loss;																		end if;			end do;		end do;		return loss;	end proc;		 	f:=proc(keypress);		SP("Score/Lose",visible,false);		SP("Score/Lose",enabled,false);		j := rand(1 .. 4); 		k := rand(1 .. 4); 		z := rand(1 .. 10); 		e := 0; 		move:=proc();				for q from 4 to 2 by -1 do				for w from 4 to 1 by -1  do					if a[q,w]=a[q-1,w] then						a[q-1,w]:=a[q-1,w]+a[q,w];						score:=score+a[q-1,w];						a[q,w]:=0;						if q-1>1 and a[q-2,w]=0 then							a[q-2,w]:=a[q-1,w];							a[q-1,w]:=0;							if q-2>1 and a[q-3,w]=0 then								a[q-3,w]:=a[q-2,w];								a[q-2,w]:=0;							end if;						end if;										elif q-1>1 and a[q,w]=a[q-2,w] and a[q-1,w]=0 then						a[q-2,w]:=a[q-2,w]+a[q,w];						score:=score+a[q-2,w];						a[q,w]:=0;						if q-2>1 and a[q-3,w]=0 then							a[q-3,w]:=a[q-2,w];							a[q-2,w]:=0;						end if;										elif q-2>1 and a[q,w]=a[q-3,w] and a[q-1,w]=0 and a[q-2,w]=0 then						a[q-3,w]:=a[q-3,w]+a[q,w];						score:=score+a[q-3,w];						a[q,w]:=0;						elif a[q-1,w]=0 then						a[q-1,w]:=a[q-1,w]+a[q,w];						a[q,w]:=0;						if q-1>1 and a[q-2,w]=0 then							a[q-2,w]:=a[q-1,w];							a[q-1,w]:=0;							if q-2>1 and a[q-3,w]=0 then								a[q-3,w]:=a[q-2,w];								a[q-2,w]:=0;							end if;						end if;										elif q-1>1 and a[q-2,w]=0 and a[q-1,w]=0 then						a[q-2,w]:=a[q-2,w]+a[q,w];						a[q,w]:=0;						if q-2>1 and a[q-3,w]=0 then							a[q-3,w]:=a[q-2,w];							a[q-2,w]:=0;						end if;										elif q-2>1 and a[q-3,w]=0 and a[q-1,w]=0 and a[q-2,w]=0 then						a[q-3,w]:=a[q-3,w]+a[q,w];						a[q,w]:=0;					end if;				end do;			end do;				end proc;  		r := j();           c := k();  		if keypress="Up" then			move();					 		elif keypress="Left" then			a:=LinearAlgebra:-Transpose(a);			move();			a:=LinearAlgebra:-Transpose(a); 		elif keypress="Right" then			a := ArrayTools:-FlipDimension(LinearAlgebra:-Transpose(a),1);			move();			a := LinearAlgebra:-Transpose(ArrayTools:-FlipDimension(a,1)); 		elif keypress="Down" then			a := ArrayTools:-FlipDimension(a, 1);			move();			a := ArrayTools:-FlipDimension(a, 1);				end if; 		if a[r, c] = 0 then 			if z() > 3 then 				a[r, c] := 2; 			else; 				a[r, c] := 4; 			end if; 		else			for q to 4 do 				for w to 4 do 					if a[q, w] <> 0 then;						e:=e+1; 					end if; 				end do; 			end do; 			if e = 16 then				loss:="True";				checklose();									a:=LinearAlgebra:-Transpose(a);				checklose();				a:=LinearAlgebra:-Transpose(a);				a := ArrayTools:-FlipDimension(LinearAlgebra:-Transpose(a),1);				checklose();				a := LinearAlgebra:-Transpose(ArrayTools:-FlipDimension(a,1));							a := ArrayTools:-FlipDimension(a, 1);				checklose();				a := ArrayTools:-FlipDimension(a, 1);				if loss="True" then		 					SP("Score/Lose",visible,"True");					SP("Score/Lose",enabled,"True");					SP("Score/Lose",caption,"You Lose!");					if score>hscore then						SP("Score/Lose",caption,"Highscore! Enter your name below!");						SP("Enter",caption,"Confirm");						SP(seq([j, enabled, true], j in ["Name","Enter","Score/Lose"])); 						SP(seq([j, visible, true], j in ["Name","Enter","Score/Lose"])); 						SP(seq([j, enabled, false], j in [seq(cat("Button",k),k=0..4)]));						SP(seq([j, visible, false], j in [seq(cat("Button",k),k=0..4)]));						if buttonpress="True" then							M:=Matrix(1, 2, [[score, hname]]):							ExportMatrix("this:///Score.csv",M);							buttonpress:="False";							reset();						end if;					else						SP("Score/Lose",caption,"Sorry, please try again.");						SP("Enter",caption,"Restart");						SP("Enter",visible,"True");						SP("Enter",enabled,"True");						SP(seq([j, enabled, false], j in [seq(cat("Button",k),k=0..4)]));						SP(seq([j, visible, false], j in [seq(cat("Button",k),k=0..4)]));									if buttonpress="True" then							buttonpress:="False";							reset();						end if;					end if;				end if;				else                   e:=0;                  while a[r, c] <> 0 do                       r := j();                       c := k();                   end do;                   if z() > 1 then                       a[r, c] := 2;                   else                       a[r, c] := 4;                   end if; 			end if; 		end if;  		matrixtotextarea(a);           SP("Score",value,score,refresh);              return a;  	end proc;end module;G:-reset();SP("Score/Lose",caption,"Click an Arrow to begin."); `

## MATLAB

`function field = puzzle2048(field) if nargin < 1 || isempty(field)    field = zeros(4);    field = addTile(field);end clcrng('shuffle') while true    oldField = field;    clc    score = displayField(field);     % check losing condition    if isGameLost(field)        sprintf('You lose with a score of %g.',score)        return    end     direction = input('Which direction? (w,a,s,d) (x for exit)\n','s');    switch direction        case 'w'            field = moveUp(field);        case 'a'            field = rot90( moveUp( rot90(field,-1) ) );        case 's'            field = flipud( moveUp( flipud(field) ) );        case 'd'            field = rot90( moveUp( rot90(field) ), -1);        case 'x'            return    end     if any(field>=2048,'all')        disp('You win!')        return    end     if ~all(field==oldField,'all')        field = addTile(field);    end end end function gameIsLost = isGameLost(field) if all(field,'all') && ...        all(conv2(field,[1, -1],'same'),'all') && ...        all(conv2(field,[1; -1],'same'),'all')    gameIsLost = true;else    gameIsLost = false;end end function field = addTile(field) freeIndices = find(~field);newIndex = freeIndices( randi(length(freeIndices)) );newNumber = 2 + 2 * (rand < 0.1);field(newIndex) = newNumber; end function score = displayField(field) % Unicode characters for box drawings% 9484: U+250C Box Drawings Light Down and Right% 9472: U+2500 Box Drawings Light Horizontal% 9474: U+2502 Box Drawings Light Vertical% 9488: U+2510 Box Drawings Light Down and Left% 9492: U+2515 Box Drawings Light Up and Right% 9496: U+2518 Box Drawings Light Up and Left% 9500: U+251C Box Drawings Light Vertical and Right% 9508: U+2524 Box Drawings Light Vertical and Left% 9516: U+252C Box Drawings Light Down and Horizontal% 9524: U+2534 Box Drawings Light Up and Horizontal% 9532: U+253C Box Drawings Light Vertical and Horizontalscore = sum(field(:));cellField = arrayfun(@num2str, field, 'UniformOutput', false);cellField = cellfun(@(x) [ char(9474) blanks(5-length(x)) x ' ' ], ...    cellField, 'UniformOutput', false);topRow = repmat('-',1,7*size(field,2)+1);topRow(1:7:end) = char(9516);topRow([1 end]) = [ char(9484) char(9488) ];midRow = topRow;midRow(1:7:end) = char(9532);midRow([1 end]) = [ char(9500) char(9508) ];botRow = topRow;botRow(1:7:end) = char(9524);botRow([1 end]) = [ char(9492) char(9496) ];charField = topRow;for iRow = cellField'    charField = [ charField; iRow{:} char(9474); midRow ];endcharField(end,:) = botRow;charField(charField == '0') = ' '; disp(charField)fprintf('\nYour score: %g\n', score) end function field = moveUp(field) for iCol = 1:size(field,2)    col = field(:,iCol);    col = removeZeros(col);    for iRow = 1:length(col)-1        if col(iRow)==col(iRow+1)            col(iRow:iRow+1) = [ 2*col(iRow); 0 ];        end    end    col = removeZeros(col);    if length(col) < length(field)        col(end+1:length(field)) = 0;    end    field(:,iCol) = col;end end function vector = removeZeros(vector) vector(vector==0) = []; end`

You can start with an empty 4 x 4 board and save the last state of the playing field with:

`field = puzzle2048();`

Or you start from a saved playing field:

`field = puzzle2048(savedField);`

## Nim

Works with: Nim Compiler version 0.19.4
`import random, strutils, terminal const  BoardLength = 4  BoardSize = BoardLength * BoardLength  Target = 2048 type  Operation = enum    opInvalid    opUp    opDown    opLeft    opRight    opQuit    opRestart   Board = object    len: Natural    largestNumber: Natural    score: Natural    rows: array[BoardLength, array[BoardLength, Natural]] func handleKey(c: char): Operation =  case c  of 'w', 'W': opUp  of 'a', 'A': opLeft  of 's', 'S': opDown  of 'd', 'D': opRight  of 'q', 'Q': opQuit  of 'r', 'R': opRestart  else: opInvalid proc getKey(): Operation =  var c = getch()  if c == '\e':    c = getch()    if c == '[':      case getch()      of 'A': opUp      of 'D': opLeft      of 'B': opDown      of 'C': opRight      else: opInvalid    else: handleKey c  else: handleKey c proc spawnRandom(b: var Board) =  if b.len < BoardSize:    var      x = rand 0..<BoardLength      y = rand 0..<BoardLength    while b.rows[y][x] != 0:      x = rand 0..<BoardLength      y = rand 0..<BoardLength    b.rows[y][x] = if rand(1.0) < 0.9: 2 else: 4    inc b.len    b.largestNumber = max(b.rows[y][x], b.largestNumber) proc initBoard(): Board =  spawnRandom result func `\$`(b: Board): string =  result = "┌────┬────┬────┬────┐\n"  for idx, val in b.rows:    for v in val:      result.add "│"      result.add center(if v != 0: \$v else: "", 4)    result.add "│\n"    if idx < high(b.rows):      result.add "├────┼────┼────┼────┤\n"    else:      result.add "└────┴────┴────┴────┘" func shift(b: var Board; o: Operation; merge = true): bool =  const BoardRange = 0..<BoardLength  var    x = 0    y = 0    vecX: range[-1..1] = 0    vecY: range[-1..1] = 0  case o  of opUp:    vecY = 1  of opDown:    vecY = -1    y = BoardLength - 1  of opLeft: vecX = 1  of opRight:    vecX = -1    x = BoardLength - 1  else: return   let    startX = x    startY = y  while x in BoardRange and y in BoardRange:    while b.len < BoardSize and x in BoardRange and y in BoardRange:      let        nextX = x + vecX        nextY = y + vecY        prevX = x - vecX        prevY = y - vecY      if b.rows[y][x] == 0:        if nextX in BoardRange and nextY in BoardRange and           b.rows[nextY][nextX] != 0:          result = true          swap b.rows[y][x], b.rows[nextY][nextX]          if prevX in BoardRange and prevY in BoardRange:            x = prevX            y = prevY            continue      x = nextX      y = nextY     if merge:      x = if vecX != 0: startX else: x      y = if vecY != 0: startY else: y      while x in BoardRange and y in BoardRange:        let          nextX = x + vecX          nextY = y + vecY        if b.rows[y][x] != 0:          if nextX in BoardRange and nextY in BoardRange and             b.rows[nextY][nextX] == b.rows[y][x]:            result = true            b.rows[y][x] *= 2            b.largestNumber = max(b.rows[y][x], b.largestNumber)            b.score += b.rows[y][x]            b.rows[nextY][nextX] = 0            dec b.len        x = nextX        y = nextY     if vecX == 0:      inc x      y = startY    elif vecY == 0:      inc y      x = startX  if merge and result: discard b.shift(o, false) func shiftable(b: Board): bool =  for row in 0..<BoardLength:    for col in 0..<BoardLength:      result = result or b.rows[row][col] == 0      if result: break      if row < BoardLength - 1:        result = result or b.rows[row][col] == b.rows[row + 1][col]      if col < BoardLength - 1:        result = result or b.rows[row][col] == b.rows[row][col + 1] when isMainModule:  randomize()  var    board = initBoard()    highscore = 0  block gameLoop:    while true:      let gameover = not board.shiftable or board.largestNumber >= Target      echo board      highscore = max(highscore, board.score)      echo "Score = ", board.score, "  Highscore = ", highscore      if not gameover:        echo "Press arrow keys or WASD to move, R to Restart, Q to Quit"      elif board.largestNumber >= Target:        echo "You win! Press R to Restart, Q to Quit"      else:        echo "Game over! Press R to Restart, Q to Quit"      while true:        let op = getKey()        case op        of opRestart:          board = initBoard()          break        of opQuit: break gameLoop        of opInvalid: continue        elif gameover: continue        else:          if board.shift op:            board.spawnRandom            break      for i in 1..BoardLength + 7:        eraseLine()        cursorUp()`

## OCaml

` let list_make x v =  let rec aux acc i =    if i <= 0 then acc else aux (v::acc) (i-1)  in  aux [] x  let pad_right n line =  let len = List.length line in  let x = n - len in  line @ (list_make x 0)  let _move_left line =  let n = List.length line in  let line = List.filter ((<>) 0) line in  let rec aux acc = function  | x::y::tl ->      if x = y      then aux (x+y::acc) tl      else aux (x::acc) (y::tl)  | x::[] ->      aux (x::acc) []  | [] ->      List.rev acc  in  pad_right n (aux [] line)  let move_left grid =  List.map _move_left grid  let move_right grid =  grid    |> List.map List.rev    |> List.map _move_left    |> List.map List.rev  let rec replace g n v =  match g with  | x::xs -> if n = 0 then v::xs else x::(replace xs (n-1) v)  | [] -> raise (Invalid_argument "replace")  (* add a new value in a random cell containing zero *)let rec new_value grid =  let zeros = ref [] in  List.iteri (fun y line ->    List.iteri (fun x v ->      if v = 0 then zeros := (x, y) :: !zeros    ) line;  ) grid;  let n = List.length !zeros in  if n = 0 then raise Exit;  let x, y = List.nth !zeros (Random.int n) in  let v = if Random.int 10 = 0 then 4 else 2 in  let line = List.nth grid y in  let new_line = replace line x v in  replace grid y new_line  (* turn counterclockwise *)let turn_ccw grid =  let y = List.length grid in  let x = List.length (List.nth grid 0) in  List.init x (fun i ->    List.init y (fun j ->      List.nth (List.nth grid j) (x-i-1)    )  )  (* turn clockwise *)let turn_cw grid =  let y = List.length grid in  let x = List.length (List.nth grid 0) in  List.init x (fun i ->    List.init y (fun j ->      List.nth (List.nth grid (y-j-1)) (i)    )  )  let move_up grid =  grid    |> turn_ccw    |> move_left    |> turn_cw  let move_down grid =  grid    |> turn_cw    |> move_left    |> turn_ccw  let display grid =  List.iter (fun line ->    print_string " [";    line      |> List.map (Printf.sprintf "%4d")      |> String.concat "; "      |> print_string;    print_endline "]"  ) grid  let () =  Random.self_init ();  let width =    try int_of_string Sys.argv.(1)    with _ -> 4  in  let line = list_make width 0 in  let grid = list_make width line in   let grid = new_value grid in  let grid = new_value grid in   print_endline {|    s -> left    f -> right    e -> up    d -> down    q -> quit  |};  let rec loop grid =    display grid;    let grid =      match read_line () with      | "s" -> move_left grid      | "f" -> move_right grid      | "e" -> move_up grid      | "d" -> move_down grid      | "q" -> exit 0      | _ -> grid    in    let grid =      try new_value grid      with Exit ->        print_endline "Game Over";        exit 0    in    loop grid  in  loop grid`
Output:
```\$ ocaml game2048.ml 4

s -> left
f -> right
e -> up
d -> down
q -> quit

[   0;    0;    0;    4]
[   0;    0;    0;    0]
[   0;    0;    0;    0]
[   0;    2;    0;    0]
d
[   0;    0;    0;    0]
[   2;    0;    0;    0]
[   0;    0;    0;    0]
[   0;    2;    0;    4]
d
[   0;    0;    0;    0]
[   4;    0;    0;    0]
[   0;    0;    0;    0]
[   2;    2;    0;    4]
f
[   0;    2;    0;    0]
[   0;    0;    0;    4]
[   0;    0;    0;    0]
[   0;    0;    4;    4]
f
[   0;    0;    0;    2]
[   0;    2;    0;    4]
[   0;    0;    0;    0]
[   0;    0;    0;    8]
```

## Perl 6

Uses termios to set the terminal options, so only compatible with POSIX terminals. This version does not include a specific "win" or "lose" condition. (though it would be trivial to add them.) You can continue to play this even after getting a 2048 tile; and if there is no valid move you can make, you can't do anything but quit.

Works with: Rakudo version 2018.05
`use Term::termios; constant \$saved   = Term::termios.new(fd => 1).getattr;constant \$termios = Term::termios.new(fd => 1).getattr;# raw mode interferes with carriage returns, so# set flags needed to emulate it manually\$termios.unset_iflags(<BRKINT ICRNL ISTRIP IXON>);\$termios.unset_lflags(< ECHO ICANON IEXTEN ISIG>);\$termios.setattr(:DRAIN); # reset terminal to original setting on exitEND { \$saved.setattr(:NOW) } constant n    = 4; # board sizeconstant cell = 6; # cell widthconstant ansi = True; # color! my @board = ( ['' xx n] xx n );my \$save  = '';my \$score = 0; constant \$top = join '─' x cell, '┌', '┬' xx n-1, '┐';constant \$mid = join '─' x cell, '├', '┼' xx n-1, '┤';constant \$bot = join '─' x cell, '└', '┴' xx n-1, '┘'; my %dir = (   "\e[A" => 'up',   "\e[B" => 'down',   "\e[C" => 'right',   "\e[D" => 'left',); my @ANSI = <0 1;97 1;93 1;92 1;96 1;91 1;95 1;94 1;30;47 1;43    1;42 1;46 1;41 1;45 1;44 1;33;43 1;33;42 1;33;41 1;33;44>; sub row (@row) { '│' ~ (join '│', @row».&center) ~ '│' } sub center (\$s){    my \$c   = cell - \$s.chars;    my \$pad = ' ' x ceiling(\$c/2);    my \$tile = sprintf "%{cell}s", "\$s\$pad";    my \$idx = \$s ?? \$s.log(2) !! 0;    ansi ?? "\e[{@ANSI[\$idx]}m\$tile\e[0m" !! \$tile;} sub draw-board {    run('clear');    print qq:to/END/;  	Press direction arrows to move. 	Press q to quit. 	\$top	{ join "\n\t\$mid\n\t", map { .&row }, @board }	\$bot 	Score: \$score END} sub squash (@c) {    my @t = grep { .chars }, @c;    map { combine(@t[\$_], @t[\$_+1]) if @t[\$_] && @t[\$_+1] == @t[\$_] }, ^@t-1;    @t = grep { .chars }, @t;    @t.push: '' while @t < n;    @t;} sub combine (\$v is rw, \$w is rw) { \$v += \$w; \$w = ''; \$score += \$v; } proto sub move (|) {*}; multi move('up') {    map { @board[*;\$_] = squash @board[*;\$_] }, ^n;} multi move('down') {    map { @board[*;\$_] = reverse squash reverse @board[*;\$_] }, ^n;} multi move('left') {    map { @board[\$_] = squash @board[\$_] }, ^n;} multi move('right') {    map { @board[\$_;*] = reverse squash reverse @board[\$_] }, ^n;} sub another {    my @empties;    for @board.kv -> \$r, @row {        @empties.push((\$r, \$_)) for @row.grep(:k, '');    }    my ( \$x, \$y ) = @empties.roll;    @board[\$x; \$y] = (flat 2 xx 9, 4).roll;} sub save () { join '|', flat @board».list } loop {    another if \$save ne save();    draw-board;    \$save = save();     # Read up to 4 bytes from keyboard buffer.    # Page navigation keys are 3-4 bytes each.    # Specifically, arrow keys are 3.    my \$key = \$*IN.read(4).decode;     move %dir{\$key} if so %dir{\$key};    last if \$key eq 'q'; # (q)uit}`

Sample output:

```	Press direction arrows to move.

Press q to quit.

┌──────┬──────┬──────┬──────┐
│  4   │  2   │      │      │
├──────┼──────┼──────┼──────┤
│  16  │  8   │      │      │
├──────┼──────┼──────┼──────┤
│  64  │  32  │  16  │      │
├──────┼──────┼──────┼──────┤
│ 128  │ 512  │ 128  │  64  │
└──────┴──────┴──────┴──────┘

Score: 6392
```

## Phix

Library: pGUI

Faithful desktop gui reproduction of the above link (https://gabrielecirulli.github.io/2048/) Now I just got figure out how to win...

`---- demo\rosetta\2048.exw--include pGUI.e Ihandle canvas, dialogcdCanvas cddbuffer, cdcanvas constant tile_colours = {#CCC0B4,   -- blank                         #EEE4DA,   -- 2                         #EDE0C8,   -- 4                         #F2B179,   -- 8                         #F59563,   -- 16                         #F67C5F,   -- 32                         #F65E3B,   -- 64                         #EDCF72,   -- 128                         #EDCC61,   -- 256                         #EDC850,   -- 512                         #EDC53F,   -- 1024                         #EDC22E}   -- 2048 -- the 4x4 board.--  note that values are [1..12] for [blank,2,4,8,..2048].--  (merging two eights is not 8+8->16 but 4+1->5, internally)sequence board integer newgame = 1 procedure add_rand(integer count)-- (nb infinite loop if board is full)integer x, y    while count do        x = rand(4)        y = rand(4)        if board[y][x]=1 then   -- blank            board[y][x] = 2+(rand(10)=10)            count -= 1        end if    end while   end procedure integer valid = 0integer prev, nxt, bxy procedure move_x(integer x, integer y, integer d)    bxy = board[x][y]    if bxy!=1 then        if bxy=prev then            board[x][y] = 1            bxy += 1            board[x][nxt] = bxy            nxt += d            prev = 13            valid = 1         else            if prev=1            or y!=nxt then                if prev!=1                and prev!=13 then                    nxt += d                end if                if y!=nxt then                    board[x][y] = 1                    board[x][nxt] = bxy                    valid = 1                end if            end if            prev = bxy        end if    end ifend procedure procedure move_y(integer x, integer y, integer d)    bxy = board[x][y]    if bxy!=1 then        if bxy=prev then            board[x][y] = 1            bxy += 1            board[nxt][y] = bxy            nxt += d            prev = 13            valid = 1         else            if prev=1            or x!=nxt then                if prev!=1                and prev!=13 then                    nxt += d                end if                if x!=nxt then                    board[x][y] = 1                    board[nxt][y] = bxy                    valid = 1                end if            end if            prev = bxy        end if    end ifend procedure function move(integer key)-- a non-zero result means it changed something.    valid = 0    if key=K_LEFT then        for x=1 to 4 do            prev = 13            nxt = 1            for y=1 to 4 do                move_x(x,y,+1)            end for        end for    elsif key=K_UP then        for y=1 to 4 do            prev = 13            nxt = 4            for x=4 to 1 by -1 do                move_y(x,y,-1)            end for        end for    elsif key=K_RIGHT then        for x=1 to 4 do            prev = 13            nxt = 4            for y=4 to 1 by -1 do                move_x(x,y,-1)            end for        end for    elsif key=K_DOWN then        for y=1 to 4 do            prev = 13            nxt = 1            for x=1 to 4 do                move_y(x,y,+1)            end for        end for    end if    return validend function function game_won()    for i=1 to length(board) do        if find(12,board[i]) then return 1 end if    end for    return 0end function constant valid_keys = {K_LEFT,K_DOWN,K_RIGHT,K_UP} function no_valid_moves()sequence saved_board = board    for i=1 to length(valid_keys) do        if move(valid_keys[i]) then            board = saved_board            return 0    -- OK        end if    end for    return 1 -- game over...end function function redraw_cb(Ihandle /*ih*/, integer /*posx*/, integer /*posy*/)integer tx, ty, bxy,        ox,oy,              -- top right coords        os,ts,              -- overall and tile size        ts2                 -- half tile, for number positioninginteger {dw,dh} = IupGetIntInt(canvas, "DRAWSIZE")    if dw>=dh then        ox = floor((dw-dh)/2)        oy = 0        os = dh    else        ox = 0        oy = floor((dh-dw)/2)        os = dw    end if    ts = floor((os-10)/4-7)    ts2 = floor(ts/2+5)-10     if newgame then        board = repeat(repeat(1,4),4)        add_rand(2)        newgame = 0    end if     cdCanvasActivate(cddbuffer)    cdCanvasSetBackground(cddbuffer, #FAF8EF)    cdCanvasClear(cddbuffer)    cdCanvasSetForeground(cddbuffer, #BBADA0)    cdCanvasRoundedBox(cddbuffer, ox+5, ox+os-5, oy+5, oy+os-5, 10, 10)     tx = ox+15    for y=1 to 4 do        ty = oy+15        for x=1 to 4 do            bxy = board[x][y]            cdCanvasSetForeground(cddbuffer, tile_colours[bxy])            cdCanvasRoundedBox(cddbuffer, tx, tx+ts-10, ty, ty+ts-10, 5, 5)            if bxy>1 then                cdCanvasSetForeground(cddbuffer, iff(bxy<=3?#776E65:#F9F6F2))                cdCanvasFont(cddbuffer, "Calibri", CD_BOLD, iff(bxy>10?32:40))                cdCanvasText(cddbuffer, tx+ts2, ty+ts2-25-iff(bxy<11?7:0), sprint(power(2,bxy-1)))             end if            ty += ts+5        end for        tx += ts+5    end for    cdCanvasFlush(cddbuffer)    return IUP_DEFAULTend function function map_cb(Ihandle ih)    cdcanvas = cdCreateCanvas(CD_IUP, ih)    cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)    {} = cdCanvasTextAlignment(cddbuffer, CD_SOUTH)     return IUP_DEFAULTend function function key_cb(Ihandle /*ih*/, atom c)    if c=K_ESC then return IUP_CLOSE end if    if find(c,valid_keys) then        if move(c) then            IupUpdate(canvas)            string mbmsg = ""            if game_won() then                mbmsg = "!!!YOU WON!!!\n\nAnother Go?"            else                add_rand(1)--              repaintWindow(main)                IupUpdate(canvas)                if no_valid_moves() then                    mbmsg = "You Lost.\n\nAnother Go?"                end if            end if            if length(mbmsg) then                if IupAlarm("Game Over",mbmsg,"Yes","No")=1 then                    newgame=1                else                    return IUP_CLOSE                end if            end if        end if        IupUpdate(canvas)    end if    return IUP_CONTINUEend function procedure main()     IupOpen()     canvas = IupCanvas("RASTERSIZE=520x540")    IupSetCallback(canvas, "MAP_CB", Icallback("map_cb"))    IupSetCallback(canvas, "ACTION", Icallback("redraw_cb"))     dialog = IupDialog(canvas,"MINSIZE=440x450")    IupSetAttribute(dialog,"TITLE","2048");    IupSetCallback(dialog, "K_ANY", Icallback("key_cb"));     IupShow(dialog)    IupSetAttribute(canvas, "RASTERSIZE", NULL)     IupMainLoop()    IupClose()end proceduremain()`

## PHP

Works from PHP5 and upwards in CLI mode.

` <?php \$game = new Game(); while(true) {    \$game->cycle();} class Game {	private \$field;	private \$fieldSize;	private \$command;	private \$error;	private \$lastIndexX, \$lastIndexY;	private \$score;	private \$finishScore; 	function __construct() {		\$this->field = array();		\$this->fieldSize = 4;		\$this->finishScore = 2048;		\$this->score = 0;		\$this->addNumber();		\$this->render();	} 	public function cycle() {		\$this->command = strtolower(\$this->readchar('Use WASD, q exits'));		\$this->cls(); 		if(\$this->processCommand()) {			\$this->addNumber();		} else {			if(count(\$this->getFreeList()) == 0 ) {				\$this->error = 'No options left!, You Lose!!';			} else {				\$this->error = 'Invalid move, try again!';			}		}		\$this->render();	} 	private function readchar(\$prompt) {		readline_callback_handler_install(\$prompt, function() {});		\$char = stream_get_contents(STDIN, 1);		readline_callback_handler_remove();		return \$char;	} 	/**	 * Insert a number in an empty spot on the field	 */	private function addNumber() {		\$freeList = \$this->getFreeList();		if(count(\$freeList) == 0) {			return;		}		\$index = mt_rand(0, count(\$freeList)-1);		\$nr = (mt_rand(0,9) == 0)? 4 : 2;		\$this->field[\$freeList[\$index]['x']][\$freeList[\$index]['y']] = \$nr;		return;	} 	/**	 * @return array(array('x' => <x>, 'y' => <y>)) with empty positions in the field	 */	private function getFreeList() {		\$freeList = array();		for(\$y =0; \$y< \$this->fieldSize;\$y++) {			for(\$x=0; \$x < \$this->fieldSize; \$x++) {				if(!isset(\$this->field[\$x][\$y])) {					\$freeList[] = array('x' => \$x, 'y' => \$y);				} elseif(\$this->field[\$x][\$y] == \$this->finishScore) {					\$this->error = 'You Win!!';				}			}		}		return \$freeList;	} 	/**	 * Process a command:	 * @return is the command valid (Did it cause a change in the field)	 */	private function processCommand() {		if(!in_array(\$this->command, array('w','a','s','d','q'))) {			\$this->error = 'Invalid Command';			return false;		}		if(\$this->command == 'q') {			echo PHP_EOL. 'Bye!'. PHP_EOL;			exit;		} 		// Determine over which axis and in which direction we move:		\$axis = 'x';		\$sDir = 1; 		switch(\$this->command) {			case 'w':				\$axis = 'y';				\$sDir = -1;				break;			case 'a':				\$sDir = -1;				break;			case 's':				\$axis = 'y';				break;			case 'd':			break;		} 		\$done = 0;		// shift all numbers in that direction		\$done += \$this->shift(\$axis, \$sDir);		// merge equal numbers in opposite direction		\$done += \$this->merge(\$axis, \$sDir * -1);		// shift merged numbers in that direction		\$done += \$this->shift(\$axis, \$sDir);		return \$done >0;	} 	private function shift(\$axis, \$dir) {		\$totalDone = 0;		for(\$i = 0; \$i <\$this->fieldSize; \$i++) {			\$done = 0;			foreach(\$this->iterate(\$axis,\$dir) as \$xy) {				if(\$xy['vDest'] === NULL && \$xy['vSrc'] !== NULL) {					\$this->field[\$xy['dX']][\$xy['dY']] = \$xy['vSrc'];					\$this->field[\$xy['sX']][\$xy['sY']] = NULL;					\$done++;				}			}			\$totalDone += \$done;			if(\$done == 0) {				// nothing to shift anymore				break;			}		}		return \$totalDone;	} 	private function merge(\$axis, \$dir) {		\$done = 0;		foreach(\$this->iterate(\$axis,\$dir) as \$xy) {			if(\$xy['vDest'] !== NULL && \$xy['vDest'] === \$xy['vSrc']) {				\$this->field[\$xy['sX']][\$xy['sY']] += \$xy['vDest'];				\$this->field[\$xy['dX']][\$xy['dY']] = NULL;				\$this->score += \$this->field[\$xy['sX']][\$xy['sY']];				\$done ++;			}		}		return \$done;	} 	/**	 * @return array List of src, dest pairs and their values to iterate over.	 */	private function iterate(\$axis, \$dir) {		\$res = array();		for(\$y = 0; \$y < \$this->fieldSize; \$y++) {			for(\$x=0; \$x < \$this->fieldSize; \$x++) {				\$item = array('sX'=> \$x,'sY' => \$y, 'dX' => \$x, 'dY' => \$y, 'vDest' => NULL,'vSrc' => NULL); 				if(\$axis == 'x') {					\$item['dX'] += \$dir;				} else {					\$item['dY'] += \$dir;				} 				if(\$item['dX'] >= \$this->fieldSize || \$item['dY'] >=\$this->fieldSize || \$item['dX'] < 0 || \$item['dY'] < 0) {					continue;				} 				\$item['vDest'] = (isset(\$this->field[\$item['dX']][\$item['dY']]))? \$this->field[\$item['dX']][\$item['dY']] : NULL;				\$item['vSrc'] = (isset(\$this->field[\$item['sX']][\$item['sY']]))? \$this->field[\$item['sX']][\$item['sY']] : NULL;				\$res[] = \$item;			}		}		if(\$dir < 0) {			\$res = array_reverse(\$res);		}		return \$res;	} 	/// RENDER /// 	/**	 * Clear terminal screen	 */	private function cls() {		echo chr(27).chr(91).'H'.chr(27).chr(91).'J';	} 	private function render() {		echo \$this->finishScore . '! Current score: '. \$this->score .PHP_EOL; 		if(!empty(\$this->error)) {			echo \$this->error . PHP_EOL;			\$this->error = NULL;		}		\$this->renderField();	} 	private function renderField() {		\$width = 5;		\$this->renderVSeperator(\$width);		for(\$y =0; \$y < \$this->fieldSize; \$y ++) {			for(\$x = 0;\$x < \$this->fieldSize; \$x++) {				echo '|';				if(!isset(\$this->field[\$x][\$y])) {					echo str_repeat(' ', \$width);					continue;				}				printf('%'.\$width.'s', \$this->field[\$x][\$y]);			}			echo '|'. PHP_EOL;			\$this->renderVSeperator(\$width);		}	} 	private function renderVSeperator(\$width) {		echo str_repeat('+'. str_repeat('-', \$width), \$this->fieldSize) .'+' .PHP_EOL;	} } `

## PicoLisp

`(load "@lib/simul.l") (seed (in "/dev/urandom" (rd 8))) (setq *G (grid 4 4)  *D NIL) (de cell ()   (use This      (while         (get            (setq This               (intern                  (pack                     (char (+ 96 (rand 1 4)))                     (rand 1 4) ) ) )            'N ) )      (=: N (if (> 90 (rand 1 100)) 2 4) ) )   (setq *D (fish '((This) (: N)) *G)) ) (de redraw (G S D)   # zeroize *G   (mapc      '((I)         (mapc '((This) (=: N NIL)) I) )      *G )   # draw again   (mapc      '((X This)         (while (and This X)            (=: N (pop 'X))            (setq This (D This)) ) )      G      S ) ) (de summ (Lst)   (mapcar      '((L)         (make            (while L               (ifn (= (car L) (cadr L))                  (link (car L))                  (link (+ (car L) (cadr L)))                  (pop 'L) )               (pop 'L) ) ) )      Lst ) ) (de vertical ()   (mapcar      '((X) (extract '((This) (: N)) X))      *G ) ) (de horizontal ()   (mapcar      '((This)         (make            (while This               (when (: N) (link @))               (setq This (east This)) ) ) )      (car *G) ) ) (de finish? ()   (nor      (fish         '((This)            (when (atom This) (= NIL (: N))) )         *G )      (find         '((L)            (find               '((This)                  (when (: N)                     (find                        '((D)                           (= (: N) (get (D This) 'N)) )                        (quote north south west east) ) ) )               L ) )         *G ) ) ) (de board (D)   (space 3)   (prin '+)   (for I G      (prin (if (D (car I)) "   +" "---+")) )   (prinl) ) (de display ()   (let G (mapcar reverse *G)      (board north)      (while (caar G)         (space 3)         (prin '|)         (for I G            (with (car I)               (prin                  (if (: N) (align 3 (: N)) "   ")                  (if (east This) " " '|) ) ) )         (prinl)         (board south)         (map pop G) )      (do 2         (prinl) ) ) ) (do 2   (cell) )(display)(loop   (case      (pack         (make            (link (key))            (while (key 100)               (link @) ) ) )      ("^[[D" #left         (redraw (summ (horizontal)) '(a1 a2 a3 a4) east) )      ("^[[C" #rigth         (redraw            (summ (mapcar reverse (horizontal)))            '(d1 d2 d3 d4)            west) )      ("^[[B" #down         (redraw (summ (vertical)) '(a1 b1 c1 d1) north) )      ("^[[A" #up         (redraw            (summ (mapcar reverse (vertical)))            '(a4 b4 c4 d4)            south) ) )   (when (diff *D (fish '((This) (: N)) *G))      (cell) )   (display)   (T (finish?) (println 'Finish))   (T (fish '((This) (= 512 (: N))) *G)      (println 'Maximum) ) )(bye)`

## Pony

Works with: ponyc version 0.10.0
` use "term"use "random"use "time" interface EdgeRow  fun val row() : Iterator[U32] ref  fun val inc() : I32 primitive TopRow is EdgeRow  fun row() : Iterator[U32] ref => let r : Array[U32] box = [0,1,2,3]    r.values()  fun inc() : I32 => 4 primitive LeftRow is EdgeRow  fun row() : Iterator[U32] ref => let r : Array[U32] box = [0,4,8,12]    r.values()  fun inc() : I32 => 1 primitive RightRow is EdgeRow  fun row() : Iterator[U32] ref => let r : Array[U32] box = [3,7,11,15]    r.values()  fun inc() : I32 => -1 primitive BottomRow is EdgeRow  fun row() : Iterator[U32] ref => let r : Array[U32] box = [12,13,14,15]    r.values()  fun inc() : I32 =>  -4 primitive LEFTprimitive RIGHTprimitive UPprimitive DOWNtype Move is (LEFT|RIGHT|UP|DOWN) class  KeyboardHandler is ANSINotify   let _game : Game tag   new iso create(game : Game tag) => _game = game    fun ref apply(term: ANSITerm ref, input: U8 val) =>     if input == 113 then       _game.quit()       term.dispose()     end   fun ref left(ctrl: Bool, alt: Bool, shift: Bool)  => _game.move(LEFT)   fun ref down(ctrl: Bool, alt: Bool, shift: Bool)  => _game.move(DOWN)   fun ref up(ctrl: Bool, alt: Bool, shift: Bool)    => _game.move(UP)   fun ref right(ctrl: Bool, alt: Bool, shift: Bool) => _game.move(RIGHT) type ROW is (U32,U32,U32,U32) primitive Merger  fun tag apply(r : ROW) : ROW =>    match r    | (0,0,0,_)            => (r._4,0,0,0)    | (0,0,_,r._3)         => (r._3<<1,0,0,0)    | (0,0,_,_)            => (r._3,r._4,0,0)    | (0,_,r._2,_)         => (r._2<<1,r._4,0,0)    | (0,_,0,r._2)         => (r._2<<1,0,0,0)    | (0,_,0,_)            => (r._2,r._4,0,0)    | (0,_,_,r._3)         => (r._2,r._3<<1,0,0)    | (0,_,_,_)            => (r._2,r._3,r._4,0)    | (_, r._1, _, r._3)   => (r._1<<1, r._3<<1, 0, 0)    | (_, r._1, 0, _)      => (r._1<<1, r._4, 0, 0)    | (_, r._1, _, _)      => (r._1<<1, r._3, r._4, 0)    | (_, 0,r._1, _)       => (r._1<<1,r._4,0,0)    | (_, 0,0, r._1)       => (r._1<<1,0,0,0)    | (_, 0,0, _)          => (r._1,r._4,0,0)    | (_, 0,_, r._3)       => (r._1, r._3<<1,0,0)    | (_, 0,_, _)          => (r._1, r._3,r._4,0)    | (_,_,r._2,_)         => (r._1, r._2<<1,r._4,0)    | (_,_,0,r._2)         => (r._1, r._2<<1,0,0)    | (_,_,0,_)            => (r._1, r._2,r._4,0)    | (_,_,_,r._3)         => (r._1, r._2,r._3<<1,0)    else       r    end/*** Game actor*/actor Game  embed _grid : Array[U32] = Array[U32].init(0, 16)  let _rand : Random = MT(Time.millis())  let _env : Env  let _board : String ref = recover String(1024) end   new create(env: Env)=>    _env = env    _add_block()    _add_block()    _draw()   fun _merge(start : U32, inc : I32) : (ROW | None) =>    var st = start.i32()    let rval : ROW = (_get(st),             _get(st + inc),                      _get(st + (inc * 2)), _get(st + (inc * 3)))    let rout = Merger(rval)    if rout is rval then None else rout end   fun ref _update(start : U32, inc : I32) : Bool =>    match _merge(start, inc)    | let rout : ROW =>        var st = start.i32()        _set(st,             rout._1)        _set(st +  inc,      rout._2)        _set(st + (inc * 2), rout._3)        _set(st + (inc * 3), rout._4)        true    else      false    end   fun ref _shift_to(edge : EdgeRow val) : Bool =>    var updated = false    for r in edge.row() do      if _update(r, edge.inc()) then        updated = true      end    end    updated   fun _fmt(i : U32) : String =>    match i    | 0 => " __ "    | 2 => "\x1B[31m  2 \x1B[0m"    | 4 => "\x1B[32m  4 \x1B[0m"    | 8 => "\x1B[33m  8 \x1B[0m"    | 16 => "\x1B[34m 16 \x1B[0m"    | 32 => "\x1B[35m 32 \x1B[0m"    | 64 => "\x1B[36m 64 \x1B[0m"    | 128 => "\x1B[37m128 \x1B[0m"    | 256 => "\x1B[41m\x1B[37m256 \x1B[0m"    | 512 => "\x1B[42m\x1B[37m512 \x1B[0m"    | 1024 => "\x1B[43m\x1B[37m1024\x1B[0m"    | 2048 => "\x1B[47m\x1B[35m\x1B[1m\x1B[5m2048\x1B[0m"    else      i.string()    end   fun ref _draw() =>    let s : String ref = _board    s.truncate(0)    var i : U32 = 0    repeat      if (i % 4) == 0 then          s.append("---------------------\n")      end      s.append(_fmt(_get(i)))      s.append(" ")      i = i + 1      if (i % 4) == 0 then          s.append("\n")      end    until i==16 end    _env.out.print(s.string())    _env.out.print("Arrow keys to move. Press (q)uit key to quit.")    fun ref _set(i:(I32|U32), v : U32) =>     try       _grid.update(i.usize(),v)     else       _env.out.print("cant update!")     end   fun _count() : U64 =>     var c : U64 = 0     for v in _grid.values() do       c = c + if v == 0 then 0 else 1 end     end     c   fun ref _add_block() =>    let c = _count()    if c == 16 then return end     var hit =  _rand.int(16 - c)    var i : U32 = 0    while i < 16 do      if (_get(i) == 0) then        if hit == 0 then          _set(i, if _rand.int(10) > 0 then 2 else 4 end)          break        end        hit = hit - 1      end      i = i + 1    end   fun _get(i : (I32|U32)) : U32 => try  _grid(i.usize()) else 0  end   fun _win() : Bool =>    for v in _grid.values() do      if v == 2048 then return true end    end    false   fun _no_moves(edge : EdgeRow val) : Bool =>    for r in edge.row() do      match _merge(r, edge.inc())      | let rout : ROW =>        if (rout._1 == 0) or (rout._2 == 0) or            (rout._3 == 0) or (rout._4 == 0) then              return false        end      end    end    true   fun _lose() : Bool =>    (_grid.size() >= 16) and    _no_moves(LeftRow) and    _no_moves(RightRow) and    _no_moves(TopRow) and    _no_moves(BottomRow)   be quit()=>    _env.out.print("Exiting.. some terminals may require <ctrl-c>")    _env.exitcode(0)    _env.input.dispose()   be move(m: Move) =>    let updated =      match m      | LEFT =>  _shift_to(LeftRow)      | RIGHT => _shift_to(RightRow)      | UP =>    _shift_to(TopRow)      | DOWN =>  _shift_to(BottomRow)      else        false      end     if _win() then      _draw()      _env.out.print("You win :)")      quit()    else      if updated then        _add_block()        _draw()      end      if _lose() then        _env.out.print("You lose :(")        quit()      end    end actor Main  new create(env: Env) =>    // unit test    ifdef "test" then      TestMain(env)      return    end    // else game    let input : Stdin tag = env.input    env.out.print("Welcome to ponylang-2048...")    let game = Game(env)    let term = ANSITerm(KeyboardHandler(game), input)     let notify : StdinNotify iso = object iso        let term: ANSITerm = term        let _in: Stdin tag = input        fun ref apply(data: Array[U8] iso) => term(consume data)        fun ref dispose() => _in.dispose()    end     input(consume notify) `

## Prolog

Works with swi-prolog, any version.

`/* -------------------------------------------------------------   Entry point, just create a blank grid and enter a 'game loop'   -------------------------------------------------------------*/play_2048 :- 	welcome_msg,	length(Grid, 16), maplist(=(' '), Grid), % create a blank grid	play(Grid, yes), !. % don't have to cut here but it makes the exit cleaner  /* -----------------------------------------------   Messages that will be printed at various points    -----------------------------------------------*/welcome_msg :- 		format('~nWelcome to the Prolog version of 2048~n~n'),	format('To play using w,s,a,d keys for movement, q to quit~n~n').	contrats_msg :- format('Congratulations, you reached 2048!~n~n').loser_msg :- format('Uh Oh, you could not quite make it to 2048...~n~n').quit_msg :- format('Bye then!~n~n').  /* -------------------   End game conditions    -------------------*/player_not_won_yet(Grid) :- maplist(dif(2048), Grid).player_wins(Grid) :- member(2048, Grid).player_loses(G) :- move(up, G, G), move(down, G, G), move(left, G, G), move(right, G, G).  /* ---------   Game loop    ---------*/	% First check if the player has reached the win condition, if not find how many spaces are leftplay(Grid, _) :- 		player_wins(Grid), 	draw_grid(Grid),	contrats_msg.play(Grid, CreateNewNum) :- 	player_not_won_yet(Grid), 	include(=(' '), Grid, Spaces), length(Spaces, NSpaces), % the number of spaces in the grid	play(Grid, CreateNewNum, NSpaces). % knowing the number of spaces, determine if there is a space, and if we need a new number, and generate.	play(Grid, no, _) :- play(Grid).play(Grid, yes, 0) :- play(Grid).play(Grid, yes, NSpaces) :- 	dif(NSpaces, 0), 	random_space(NSpaces, Grid, GridWithRandom), 	play(GridWithRandom). % with the new number on the grid we can tell if the player has lost the game yet% if not, draw the grid and get the next action by the player play(Grid) :- 	player_loses(Grid), 	draw_grid(Grid), 	loser_msg.		play(Grid) :- 	\+ player_loses(Grid),	draw_grid(Grid),	next_move_by_player(Move),	player_made_move(Grid, Move). % determine the result of player move	player_made_move(_, quit).player_made_move(Grid, Move) :- 	dif(Move, quit), 	move(Move, Grid, Grid), % The move creating the same grid indicates that no merge was done	play(Grid, no). % don't create a new numberplayer_made_move(Grid, Move) :- 	dif(Move, quit), 	move(Move, Grid, MovedGrid), 	dif(Grid, MovedGrid),	play(MovedGrid, yes).  /* ---------------------------------------   Get the next move from the player input    ---------------------------------------*/	next_move_by_player(Move) :- 	repeat, 	get_single_char(Char), 	key_move(Char, Move). % valid keys are: up = 'w', down = 's', left = 'a' right = 'd', quit = 'q'key_move(119, up). key_move(115, down).	key_move(97, left). key_move(100, right). key_move(113, quit).  /* ------------------   Draw the Game grid    ------------------*/draw_grid([A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4]) :-	format(   '+-------------------+~n'),	row([A1,A2,A3,A4]), 	row([B1,B2,B3,B4]), 	row([C1,C2,C3,C4]), 	maplist(draw, [D1,D2,D3,D4]),	format('¦~n+-------------------+~n~n~n'). row([A,B,C,D]) :- maplist(draw, [A,B,C,D]), format('¦~n¦----+----+----+----¦~n').	 draw(' ') :- format('¦    ').draw(X) :- member(X,[2,4,8]), format('¦  ~d ', X).draw(X) :- member(X,[16,32,64]), format('¦ ~d ', X).draw(X) :- member(X,[128,256,512]), format('¦ ~d', X).draw(X) :- member(X,[1024,2048]), format('¦~d', X).  /* ----------------------------------------   Populate a random space with a new value    ----------------------------------------*/random_space(0, G, G).random_space(1, Grid, GridWithRandom) :- 	four_or_two(V), 	select(' ', Grid, V, GridWithRandom).random_space(N, Grid, GridWithRandom) :-	N > 1,	four_or_two(V),	random(1, N, P),		replace_space(P, V, Grid, GridWithRandom). replace_space(0, V, [' '|T], [V|T]).replace_space(P, V, [' '|T], [' '|R]) :-	succ(NextP, P),	replace_space(NextP, V, T, R).replace_space(P, V, [H|T], [H|R]) :-	dif(' ', H), replace_space(P, V, T, R). four_or_two(V) :- random(1, 10, IsFour), IsFour = 1 -> V = 4 ; V = 2.  /* ------------------------------------------   Process a game move based on the direction    ------------------------------------------*/move(Direction, UnMoved, Moved) :-	map_move(Direction, UnMoved, UnMovedMapped),	maplist(combine_row, UnMovedMapped, MovedMapped),	map_move(Direction, Moved, MovedMapped). % convert the array to a set of lists that can be moved in the same% direction. This can be reversed after the move is completed.map_move(up, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[D1,C1,B1,A1],[D2,C2,B2,A2],[D3,C3,B3,A3],[D4,C4,B4,A4]]).map_move(down, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[A1,B1,C1,D1],[A2,B2,C2,D2],[A3,B3,C3,D3],[A4,B4,C4,D4]]).map_move(left, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[A4,A3,A2,A1],[B4,B3,B2,B1],[C4,C3,C2,C1],[D4,D3,D2,D1]]).map_move(right, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[A1,A2,A3,A4],[B1,B2,B3,B4],[C1,C2,C3,C4],[D1,D2,D3,D4]]). % remove all the spaces, then put them at the front of the listcombine_row(UnMoved, Moved) :-	partition(=(' '), UnMoved, Blank, Set),	append(Blank, Set, ReadyToMove),	combine(ReadyToMove, Moved). % combine based on the rules of the game.combine([A,B,C,D], [A,B,C,D]) :- dif(A,B), dif(B,C), dif(C,D).	combine([A,A,B,B], [' ',' ',Ad,Bd]) :- dbl(A,Ad), dbl(B,Bd).combine([A,B,C,C], [' ',A,B,Cd]) :- dif(A,B), dbl(C,Cd).combine([A,B,B,C], [' ',A,Bd,C]) :-	dif(B,C), dbl(B,Bd).combine([A,A,B,C], [' ',Ad,B,C]) :- dif(A,B), dif(B,C), dbl(A, Ad).combine([A,B,C,C], [' ',A,B,Cd]) :- dif(A,B), dif(B,C), dbl(C,Cd). % this could be done using maths, but it is more prology this way.dbl(' ', ' '). dbl(2,4). dbl(4,8). dbl(8,16). dbl(16,32). dbl(32,64). dbl(64,128). dbl(128,256). dbl(256,512). dbl(512,1028). dbl(1028,2048). `
Output:
```?- play_2048.

Welcome to the Prolog version of 2048

To play using w,s,a,d keys for movement, q to quit

+-------------------+
¦    ¦    ¦    ¦    ¦
¦----+----+----+----¦
¦    ¦    ¦    ¦  2 ¦
¦----+----+----+----¦
¦    ¦    ¦    ¦    ¦
¦----+----+----+----¦
¦    ¦    ¦    ¦    ¦
+-------------------+```

## Python

` #!/usr/bin/env python3 import cursesfrom random import randrange, choice # generate and place new tilefrom collections import defaultdict letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']actions_dict = dict(zip(letter_codes, actions * 2)) def get_user_action(keyboard):    	char = "N"	while char not in actions_dict:    		char = keyboard.getch()	return actions_dict[char] def transpose(field):	return [list(row) for row in zip(*field)] def invert(field):	return [row[::-1] for row in field] class GameField(object):	def __init__(self, height=4, width=4, win=2048):		self.height = height		self.width = width		self.win_value = win		self.score = 0		self.highscore = 0		self.reset() 	def reset(self):		if self.score > self.highscore:			self.highscore = self.score		self.score = 0		self.field = [[0 for i in range(self.width)] for j in range(self.height)]		self.spawn()		self.spawn() 	def move(self, direction):		def move_row_left(row):			def tighten(row): # squeese non-zero elements together				new_row = [i for i in row if i != 0]				new_row += [0 for i in range(len(row) - len(new_row))]				return new_row 			def merge(row):				pair = False				new_row = []				for i in range(len(row)):					if pair:						new_row.append(2 * row[i])						self.score += 2 * row[i]						pair = False					else:						if i + 1 < len(row) and row[i] == row[i + 1]:							pair = True							new_row.append(0)						else:							new_row.append(row[i])				assert len(new_row) == len(row)				return new_row			return tighten(merge(tighten(row))) 		moves = {}		moves['Left']  = lambda field:								\				[move_row_left(row) for row in field]		moves['Right'] = lambda field:								\				invert(moves['Left'](invert(field)))		moves['Up']    = lambda field:								\				transpose(moves['Left'](transpose(field)))		moves['Down']  = lambda field:								\				transpose(moves['Right'](transpose(field))) 		if direction in moves:			if self.move_is_possible(direction):				self.field = moves[direction](self.field)				self.spawn()				return True			else:				return False 	def is_win(self):		return any(any(i >= self.win_value for i in row) for row in self.field) 	def is_gameover(self):		return not any(self.move_is_possible(move) for move in actions) 	def draw(self, screen):		help_string1 = '(W)Up (S)Down (A)Left (D)Right'		help_string2 = '     (R)Restart (Q)Exit'		gameover_string = '           GAME OVER'		win_string = '          YOU WIN!'		def cast(string):			screen.addstr(string + '\n') 		def draw_hor_separator():			top = '┌' + ('┬──────' * self.width + '┐')[1:]			mid = '├' + ('┼──────' * self.width + '┤')[1:]			bot = '└' + ('┴──────' * self.width + '┘')[1:]			separator = defaultdict(lambda: mid)			separator[0], separator[self.height] = top, bot			if not hasattr(draw_hor_separator, "counter"):				draw_hor_separator.counter = 0			cast(separator[draw_hor_separator.counter])			draw_hor_separator.counter += 1 		def draw_row(row):			cast(''.join('│{: ^5} '.format(num) if num > 0 else '|      ' for num in row) + '│') 		screen.clear()		cast('SCORE: ' + str(self.score))		if 0 != self.highscore:			cast('HIGHSCORE: ' + str(self.highscore))		for row in self.field:			draw_hor_separator()			draw_row(row)		draw_hor_separator()		if self.is_win():			cast(win_string)		else:			if self.is_gameover():				cast(gameover_string)			else:				cast(help_string1)		cast(help_string2) 	def spawn(self):		new_element = 4 if randrange(100) > 89 else 2		(i,j) = choice([(i,j) for i in range(self.width) for j in range(self.height) if self.field[i][j] == 0])		self.field[i][j] = new_element 	def move_is_possible(self, direction):		def row_is_left_movable(row): 			def change(i): # true if there'll be change in i-th tile				if row[i] == 0 and row[i + 1] != 0: # Move					return True				if row[i] != 0 and row[i + 1] == row[i]: # Merge					return True				return False			return any(change(i) for i in range(len(row) - 1)) 		check = {}		check['Left']  = lambda field:								\				any(row_is_left_movable(row) for row in field) 		check['Right'] = lambda field:								\				 check['Left'](invert(field)) 		check['Up']    = lambda field:								\				check['Left'](transpose(field)) 		check['Down']  = lambda field:								\				check['Right'](transpose(field)) 		if direction in check:			return check[direction](self.field)		else:			return False def main(stdscr):	curses.use_default_colors()	game_field = GameField(win=32)	state_actions = {} # Init, Game, Win, Gameover, Exit	def init():		game_field.reset()		return 'Game' 	state_actions['Init'] = init 	def not_game(state):		game_field.draw(stdscr)		action = get_user_action(stdscr)		responses = defaultdict(lambda: state)		responses['Restart'], responses['Exit'] = 'Init', 'Exit'		return responses[action] 	state_actions['Win'] = lambda: not_game('Win')	state_actions['Gameover'] = lambda: not_game('Gameover') 	def game():		game_field.draw(stdscr)		action = get_user_action(stdscr)		if action == 'Restart':			return 'Init'		if action == 'Exit':			return 'Exit'		if game_field.move(action): # move successful			if game_field.is_win():				return 'Win'			if game_field.is_gameover():				return 'Gameover'		return 'Game' 	state_actions['Game'] = game 	state = 'Init'	while state != 'Exit':		state = state_actions[state]() curses.wrapper(main) `

## QB64

` _DEFINE A-Z AS _INTEGER64DIM SHARED Grid(0 TO 5, 0 TO 5) AS INTEGERCONST Left = 19200CONST Right = 19712CONST Down = 20480CONST Up = 18432CONST ESC = 27CONST LCtrl = 100306CONST RCtrl = 100305 InitMakeNewGameDO    _LIMIT 30    ShowGrid    CheckInput flag    IF flag THEN GetNextNumber    _DISPLAYLOOP SUB CheckInput (flag)flag = 0k = _KEYHITSELECT CASE k    CASE ESC: SYSTEM    CASE 83, 115 'S        IF _KEYDOWN(LCtrl) OR _KEYDOWN(RCtrl) THEN MakeNewGame    CASE Left        MoveLeft        flag = -1 'we hit a valid move key.  Even if we don't move, get a new number    CASE Up        MoveUp        flag = -1    CASE Down        MoveDown        flag = -1    CASE Right        MoveRight        flag = -1END SELECTEND SUB SUB MoveDown'first move everything left to cover the blank spacesDO    moved = 0    FOR y = 4 TO 1 STEP -1        FOR x = 1 TO 4            IF Grid(x, y) = 0 THEN 'every point above this moves down                FOR j = y TO 1 STEP -1                    Grid(x, j) = Grid(x, j - 1)                    IF Grid(x, j) <> 0 THEN moved = -1                NEXT            END IF        NEXT    NEXT    IF moved THEN y = y + 1 'recheck the same columnLOOP UNTIL NOT movedFOR y = 4 TO 1 STEP -1    FOR x = 1 TO 4        IF Grid(x, y) <> 0 AND Grid(x, y) = Grid(x, y - 1) THEN 'add them together and every point above this moves            Grid(x, y) = Grid(x, y) * 2            FOR j = y - 1 TO 1                Grid(x, j) = Grid(x, j - 1)            NEXT        END IF    NEXTNEXTEND SUB SUB MoveLeft'first move everything to cover the blank spacesDO    moved = 0    FOR x = 1 TO 4        FOR y = 1 TO 4            IF Grid(x, y) = 0 THEN 'every point right of this moves left                FOR j = x TO 4                    Grid(j, y) = Grid(j + 1, y)                    IF Grid(j, y) <> 0 THEN moved = -1                NEXT            END IF        NEXT    NEXT    IF moved THEN x = x - 1 'recheck the same rowLOOP UNTIL NOT movedFOR x = 1 TO 4    FOR y = 1 TO 4        IF Grid(x, y) <> 0 AND Grid(x, y) = Grid(x + 1, y) THEN 'add them together and every point right of this moves left            Grid(x, y) = Grid(x, y) * 2            FOR j = x + 1 TO 4                Grid(j, y) = Grid(j + 1, y)            NEXT        END IF    NEXTNEXTEND SUB SUB MoveUp'first move everything to cover the blank spacesDO    moved = 0    FOR y = 1 TO 4        FOR x = 1 TO 4            IF Grid(x, y) = 0 THEN 'every point below of this moves up                FOR j = y TO 4                    Grid(x, j) = Grid(x, j + 1)                    IF Grid(x, j) <> 0 THEN moved = -1                NEXT            END IF        NEXT    NEXT    IF moved THEN y = y - 1 'recheck the same columnLOOP UNTIL NOT movedFOR y = 1 TO 4    FOR x = 1 TO 4        IF Grid(x, y) <> 0 AND Grid(x, y) = Grid(x, y + 1) THEN 'add them together and every point below this moves            Grid(x, y) = Grid(x, y) * 2            FOR j = y + 1 TO 4                Grid(x, j) = Grid(x, j + 1)            NEXT            Grid(x, 4) = 0        END IF    NEXTNEXTEND SUB SUB MoveRight'first move everything to cover the blank spacesDO    moved = 0    FOR x = 4 TO 1 STEP -1        FOR y = 1 TO 4            IF Grid(x, y) = 0 THEN 'every point right of this moves left                FOR j = x TO 1 STEP -1                    Grid(j, y) = Grid(j - 1, y)                    IF Grid(j, y) <> 0 THEN moved = -1                NEXT            END IF        NEXT    NEXT    IF moved THEN x = x - 1 'recheck the same rowLOOP UNTIL NOT moved FOR x = 4 TO 1 STEP -1    FOR y = 1 TO 4        IF Grid(x, y) <> 0 AND Grid(x, y) = Grid(x - 1, y) THEN 'add them together and every point right of this moves left            Grid(x, y) = Grid(x, y) * 2            FOR j = x - 1 TO 1 STEP -1                Grid(j, y) = Grid(j - 1, y)            NEXT        END IF    NEXTNEXTEND SUB SUB ShowGrid'SUB MakeBox (Mode AS INTEGER, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER,'Caption AS STRING, FontColor AS _UNSIGNED LONG, FontBackground AS _UNSIGNED LONG,'BoxColor AS _UNSIGNED LONG, BoxHighLight AS _UNSIGNED LONG, XOffset AS INTEGER, YOffset AS INTEGER)w = 120h = 120FOR x = 1 TO 4    FOR y = 1 TO 4        t\$ = LTRIM\$(STR\$(Grid(x, y)))        IF t\$ = "0" THEN t\$ = ""        MakeBox 4, (x - 1) * w, (y - 1) * h, w, h, t\$, -1, 0, 0, -1, 0, 0    NEXTNEXTEND SUB SUB Initws = _NEWIMAGE(480, 480, 32)SCREEN ws_DELAY 1_TITLE "Double Up"_SCREENMOVE _MIDDLERANDOMIZE TIMERf& = _LOADFONT("C:\Windows\Fonts\courbd.ttf", 32, "MONOSPACE")_FONT f& END SUB SUB MakeNewGameFOR x = 1 TO 4    FOR y = 1 TO 4        Grid(x, y) = 0    NEXTNEXTGetNextNumberGetNextNumberEND SUB SUB GetNextNumberFOR x = 1 TO 4    FOR y = 1 TO 4        IF Grid(x, y) = 0 THEN valid = -1    NEXTNEXTIF valid THEN 'If all the grids are full, we can't add any more numbers    'This doesn't mean the game is over, as the player may be able to    DO        x = _CEIL(RND * 4)        y = _CEIL(RND * 4)    LOOP UNTIL Grid(x, y) = 0    Grid(x, y) = 2END IFEND SUB SUB MakeBox (Mode AS INTEGER, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, Caption AS STRING, FontColor AS _UNSIGNED LONG, FontBackground AS _UNSIGNED LONG, BoxColor AS _UNSIGNED LONG, BoxHighLight AS _UNSIGNED LONG, XOffset AS INTEGER, YOffset AS INTEGER) 'This is an upgrade version of my original Button routine.'It's more versitile (but complex) than the original.'Mode 0 (or any unsupported number) will tell the box to size itself from X1,Y1 to X2,Y2'Mode 1 will tell the box to autosize itself according to whatever text is placed within it.'Mode 2 will tell the box to use X2 and Y2 as relative coordinates and not absolute coordinates.'Mode 3 will tell the box to autocenter text with X2, Y2 being absolute coordinates.'Mode 4 will tell the box to autocenter text with X2, Y2 being relative coordinates.'Mode otherwise is unused, but available for expanded functionality.'X1 carries the X location of where we want to place our box on the screen.'Y2 carries the Y location of where we want to place our box on the screen.'X2 is the X boundry of our box on the screen, depending on our mode.'Y2 is the Y boundry of our box on the screen, depending on our mode. 'Caption is the text that we want our box to contain. 'FontColor is our font color for our caption'FontBackground is the font background color for our caption'NOTE: IF FONTCOLOR OR FONTBACKGROUND IS SET TO ZERO, THEY WILL **NOT** AFFECT THE COLOR BEHIND THEM.'This can be used to mimic the function of _KEEPBACKGROUND, _FILLBACKGROUND, or _ONLYBACKGROUND 'BoxColor is our box color'BoxHighlight is our box highligh colors'NOTE: SAME WITH BOXCOLOR AND BOXHIGHLIGHT.  IF SET TO ZERO, THEY WILL HAVE **NO** COLOR AT ALL TO THEM, AND WILL NOT AFFECT THE BACKGROUND OF ANYTHING BEHIND THEM. 'XOffset is used to offset our text # pixels from the X1 top.'YOffset is used to offset our text # pixels from the Y1 top.'These can be used to place our text wherever we want on our box.'But remember, if Mode = 3 or 4, the box will autocenter the text and ignore these parameters completely. DIM BoxBlack AS _UNSIGNED LONG dc& = _DEFAULTCOLOR: bg& = _BACKGROUNDCOLORIF Black <> 0 THEN    'We have black either as a CONST or a SHARED color    BoxBlack = BlackELSE    'We need to define what Black is for our box.    BoxBlack = _RGB32(0, 0, 0)END IF IF _FONTWIDTH <> 0 THEN cw = _FONTWIDTH * LEN(Caption) ELSE cw = _PRINTWIDTH(Caption)ch = _FONTHEIGHT tx1 = x1: tx2 = x2: ty1 = y1: ty2 = y2SELECT CASE Mode    CASE 0        'We use the X2, Y2 coordinates provided as absolute coordinates    CASE 1        tx2 = tx1 + cw + 8        ty2 = ty1 + ch + 8        XOffset = 5: YOffset = 5    CASE 2        tx2 = tx1 + x2        ty2 = ty1 + y2    CASE 3        XOffset = (tx2 - tx1 - cw) \ 2        YOffset = (ty2 - ty1 - ch) \ 2    CASE 4        tx2 = tx1 + x2        ty2 = ty1 + y2        XOffset = (tx2 - tx1) \ 2 - cw \ 2        YOffset = (ty2 - ty1 - ch) \ 2END SELECTLINE (tx1, ty1)-(tx2, ty2), BoxBlack, BFLINE (tx1 + 1, ty1 + 1)-(tx2 - 1, ty2 - 1), BoxHighLight, BLINE (tx1 + 2, ty1 + 2)-(tx2 - 2, ty2 - 2), BoxHighLight, BLINE (tx1 + 3, ty1 + 3)-(tx2 - 3, ty2 - 3), BoxBlack, BLINE (tx1, ty1)-(tx1 + 3, ty1 + 3), BoxBlackLINE (tx2, ty1)-(tx2 - 3, ty1 + 3), BoxBlackLINE (tx1, ty2)-(tx1 + 3, ty2 - 3), BoxBlackLINE (tx2, ty2)-(tx2 - 3, ty2 - 3), BoxBlackLINE (tx1 + 3, y1 + 3)-(tx2 - 3, ty2 - 3), BoxColor, BFCOLOR FontColor, FontBackground_PRINTSTRING (tx1 + XOffset, ty1 + YOffset), Caption\$COLOR dc&, bg&END SUB `

## R

orginal R package : https://github.com/ThinkRstat/r2048

` GD <- function(vec) {    c(vec[vec != 0], vec[vec == 0])}DG <- function(vec) {    c(vec[vec == 0], vec[vec != 0])} DG_ <- function(vec, v = TRUE) {    if (v)         print(vec)    rev(GD_(rev(vec), v = FALSE))} GD_ <- function(vec, v = TRUE) {    if (v) {        print(vec)    }    vec2 <- GD(vec)    # on cherche les 2 cote a cote    pos <- which(vec2 == c(vec2[-1], 9999))    # put pas y avoir consécutif dans pos    pos[-1][which(abs(pos - c(pos[-1], 999)) == 1)]    av <- which(c(0, c(pos[-1], 9) - pos) == 1)    if (length(av) > 0) {        pos <- pos[-av]    }    vec2[pos] <- vec2[pos] + vec2[pos + 1]    vec2[pos + 1] <- 0    GD(vec2) } H_ <- function(base) {    apply(base, MARGIN = 2, FUN = GD_, v = FALSE)}B_ <- function(base) {    apply(base, MARGIN = 2, FUN = DG_, v = FALSE)}G_ <- function(base) {    t(apply(base, MARGIN = 1, FUN = GD_, v = FALSE))}D_ <- function(base) {    t(apply(base, MARGIN = 1, FUN = DG_, v = FALSE))} H <- function(base) {    apply(base, MARGIN = 2, FUN = GD, v = FALSE)}B <- function(base) {    apply(base, MARGIN = 2, FUN = DG, v = FALSE)}G <- function(base) {    t(apply(base, MARGIN = 1, FUN = GD, v = FALSE))}D <- function(base) {    t(apply(base, MARGIN = 1, FUN = DG, v = FALSE))} add2or4 <- function(base, p = 0.9) {    lw <- which(base == 0)    if (length(lw) > 1) {        tirage <- sample(lw, 1)    } else {        tirage <- lw    }    base[tirage] <- sample(c(2, 4), 1, prob = c(p, 1 - p))    base}print.dqh <- function(base) {    cat("\n\n")    for (i in 1:nrow(base)) {        cat(paste("     ", base[i, ], " "))        cat("\n")    }    cat("\n")}   # -*- coding: utf-8 -*-#' @encoding UTF-8#' @title run_2048#' @description The 2048 game#' @param nrow nomber of row#' @param ncol numver of col#' @param p probability to obtain a 2 (1-p) is the probability to obtain a 4#' @examples#' \dontrun{#' run_2048()#' }#' @export  run_2048 <- function(nrow, ncol, p = 0.9) {       help <- function() {        cat("   *** KEY BINDING ***  \n\n")        cat("press ECHAP to quit\n\n")        cat("choose moove E (up) ; D (down) ; S (left); F (right) \n")        cat("choose moove 8 (up) ; 2 (down) ; 4 (left); 6 (right) \n")        cat("choose moove I (up) ; K (down) ; J (left); L (right) \n\n\n")     }      if (missing(nrow) & missing(ncol)) {        nrow <- ncol <- 4    }    if (missing(nrow)) {        nrow <- ncol    }    if (missing(ncol)) {        ncol <- nrow    }     base <- matrix(0, nrow = nrow, ncol = ncol)     while (length(which(base == 2048)) == 0) {        base <- add2or4(base, p = p)        # print(base)         class(base) <- "dqh"        print(base)        flag <- sum((base == rbind(base[-1, ], 0)) + (base == rbind(0,             base[-nrow(base), ])) + (base == cbind(base[, -1], 0)) + (base ==             cbind(0, base[, -nrow(base)])))        if (flag == 0) {             break        }         y <- character(0)        while (length(y) == 0) {            cat("\n", "choose moove E (up) ; D (down) ; s (left); f (right) OR H for help",                 "\n")  # prompt            y <- scan(n = 1, what = "character")        }          baseSAVE <- base        base <- switch(EXPR = y, E = H_(base), D = B_(base), S = G_(base),             F = D_(base), e = H_(base), d = B_(base), s = G_(base), f = D_(base),             `8` = H_(base), `2` = B_(base), `4` = G_(base), `6` = D_(base),             H = help(), h = help(), i = H_(base), k = B_(base), j = G_(base),             l = D_(base), I = H_(base), K = B_(base), J = G_(base), L = D_(base))        if (is.null(base)) {            cat(" wrong KEY \n")            base <- baseSAVE        }       }     if (sum(base >= 2048) > 1) {        cat("YOU WIN ! \n")    } else {        cat("YOU LOOSE \n")    }}  `

## Racket

Original repo: https://github.com/danprager/2048 Play the RacketScript fork online here: http://rapture.twistedplane.com:8080/#example/2048-game

` ;; LICENSE: See License file LICENSE (MIT license);;;; Repository: https://github.com/danprager/2048;;;; Copyright 2014: Daniel Prager;;                 [email protected];;;; This is a largely clean-room, functional implementation in Racket ;; of the game 2048 by Gabriele Cirulli, based on 1024 by Veewo Studio,;; and conceptually similar to Threes by Asher Vollmer.;;;;;; HOW TO PLAY: ;;   * Use your arrow keys to slide the tiles. ;;   * When two tiles with the same number touch, they merge into one!;;   * Press <space> to rotate the board.;; #lang racket (require rackunit         2htdp/image         (rename-in 2htdp/universe                    [left left-arrow]                    [right right-arrow]                    [up up-arrow]                    [down down-arrow]))  (define *side* 4)              ; Side-length of the grid(define *time-limit* #f)       ; Use #f for no time limit, or number of seconds (define *amber-alert* 60)      ; Time indicator goes orange when less than this number of seconds remaining(define *red-alert* 10)        ; Time indicator goes red when less than this number of seconds remaining (define *tile-that-wins* 2048) ; You win when you get a tile = this number(define *magnification* 2)     ; Scales the game board (define (set-side! n)  (set! *side* n)) ;;;; Numbers can be displayed with substiture text. Just edit this table...;;(define *text*  '((0 "")    (2 "2"))) ;; Color scheme ;;;; From https://github.com/gabrielecirulli/2048/blob/master/style/main.css;;(define *grid-color* (color #xbb #xad #xa0)) (define *default-tile-bg-color* (color #x3c #x3a #x32))(define *default-tile-fg-color* 'white) (define *tile-bg-colors*  (map (lambda (x)         (match-define (list n r g b) x)         (list n (color r g b)))       '((0 #xcc #xc0 #xb3)         (2 #xee #xe4 #xda)         (4 #xed #xe0 #xc8)         (8 #xf2 #xb1 #x79)         (16 #xf5 #x95 #x63)         (32 #xf6 #x7c #x5f)         (64 #xf6 #x5e #x3b)         (128 #xed #xcf #x72)         (256 #xed #xcc #x61)         (512 #xed #xc8 #x50)         (1024 #xed #xc5 #x3f)         (2048 #xed #xc2 #x2e)))) (define *tile-fg-colors*  '((0 dimgray)    (2 dimgray)    (4 dimgray)    (8 white)    (16 white)    (32 white)    (64 white)    (128 white)    (256 white)    (512 white)    (1024 white)    (2048 white))) ;;--------------------------------------------------------------------;; Rows may be represented as lists, with 0s representing empty spots.;; (define (nonzero? x) (not (zero? x))) ;; Append padding to lst to make it n items long;;(define (pad-right lst padding n)  (append lst (make-list (- n (length lst)) padding))) ;; Slide items towards the head of the list, doubling adjacent pairs;; when no item is a 0.;;;; E.g. (combine '(2 2 2 4 4)) -> '(4 2 8);;(define (combine lst)  (cond [(<= (length lst) 1) lst]        [(= (first lst) (second lst))         (cons (* 2 (first lst)) (combine (drop lst 2)))]        [else (cons (first lst) (combine (rest lst)))])) ;; Total of new elements introduced by combining.;;;; E.g. (combine-total '(2 2 2 4 4)) -> 4 + 8 = 12;;(define (combine-total lst)  (cond [(<= (length lst) 1) 0]        [(= (first lst) (second lst))         (+ (* 2 (first lst)) (combine-total (drop lst 2)))]        [else (combine-total (rest lst))])) ;; Slide towards the head of the list, doubling pairs, 0 are;; allowed (and slid through), and length is preserved by;; padding with 0s.;;;; E.g. (slide-left '(2 2 2 0 4 4)) -> '(4 2 8 0 0 0);;(define (slide-left row)  (pad-right (combine (filter nonzero? row)) 0 (length row))) ;; Slide towards the tail of the list:;;;; E.g. (slide-right '(2 2 0 0 4 4)) -> '(0 0 0 0 0 4 8);;(define (slide-right row) (reverse (slide-left (reverse row))))  ;;--------------------------------------------------------------------;; We use a sparse representation for transitions in a row.;;;; Moves take the form '(value initial-position final-position) ;;(define (moves-row-left row [last #f] [i 0] [j -1])  (if (null? row)       null      (let ([head (first row)])        (cond [(zero? head) (moves-row-left (rest row) last (add1 i) j)]              [(equal? last head)                (cons (list head i j)                     (moves-row-left (rest row) #f (add1 i) j))]              [else (cons (list head i (add1 j))                          (moves-row-left (rest row) head (add1 i) (add1 j)))])))) ;; Convert a row into the sparse representaiton without any sliding.;;;; E.g. (moves-row-none '(0 2 0 4)) -> '((2 1 1) (4 3 3));;(define (moves-row-none row)  (for/list ([value row]             [i (in-naturals)]             #:when (nonzero? value))    (list value i i))) ;; Reverse all moves so that:;;;; '(value initial final) -> '(value (- n initial 1) (- n final 1);;(define (reverse-moves moves n)  (define (flip i) (- n i 1))  (map (λ (m)         (match-define (list a b c) m)         (list a (flip b) (flip c)))       moves)) (define (transpose-moves moves)  (for/list ([m moves])    (match-define (list v (list a b) (list c d)) m)    (list v (list b a) (list d c)))) (define (moves-row-right row [n *side*])  (reverse-moves (moves-row-left (reverse row)) n)) ;;--------------------------------------------------------------------;; Lift the sparse representation for transitions;; up to two dimensions...;;;; '(value initial final) -> '(value (x initial) (x final));;(define (add-row-coord i rows)  (for/list ([r rows])    (match-define (list a b c) r)    (list a (list i b) (list i c)))) (define (transpose lsts)  (apply map list lsts)) ;; Slide the entire grid in the specified direction;;(define (left grid)  (map slide-left grid)) (define (right grid)  (map slide-right grid)) (define (up grid)  ((compose transpose left transpose) grid)) (define (down grid)  ((compose transpose right transpose) grid)) ;; Calculate the change to score from sliding the grid left or right.;;(define (score-increment grid)  (apply + (map (λ (row)                   (combine-total (filter nonzero? row)))                grid))) ;; Slide the grid in the specified direction and ;; determine the transitions of the tiles. ;;;; We'll use these operations to animate the sliding of the tiles.;;(define (moves-grid-action grid action)  (let ([n (length (first grid))])    (apply append           (for/list ([row grid]                      [i (in-range n)])             (add-row-coord i (action row)))))) (define (moves-grid-left grid)  (moves-grid-action grid moves-row-left)) (define (moves-grid-right grid)  (moves-grid-action grid moves-row-right)) (define (moves-grid-up grid)  ((compose transpose-moves moves-grid-left transpose) grid)) (define (moves-grid-down grid)  ((compose transpose-moves moves-grid-right transpose) grid)) ;; Rotating the entire grid doesn't involve sliding.;; It's a convenience to allow the player to view the grid from a different;; orientation.(define (moves-grid-rotate grid)  (let ([n (length (first grid))])    (for/list ([item (moves-grid-action grid moves-row-none)])      (match-define (list v (list i j) _) item)      (list v (list i j) (list j (- n i 1)))))) ;; Chop a list into a list of sub-lists of length n. Used to move from;; a flat representation of the grid into a list of rows.;; ;;(define (chop lst [n *side*])  (if (<= (length lst) n)       (list lst)      (cons (take lst n) (chop (drop lst n) n))))  ;; The next few functions are used to determine where to place a new;; number in the grid...;; ;; How many zeros in the current state?;;(define (count-zeros state)  (length (filter zero? state))) ;; What is the absolute index of the nth zero in lst?;;;; E.g. (index-of-nth-zero '(0 2 0 4) 1 2)) 1) -> 2;;(define (index-of-nth-zero lst n)  (cond [(null? lst) #f]        [(zero? (first lst))          (if (zero? n)             0             (add1 (index-of-nth-zero (rest lst) (sub1 n))))]        [else (add1 (index-of-nth-zero (rest lst) n))])) ;; Place the nth zero in the lst with val.;;;; E.g. (replace-nth-zero '(0 2 0 4) 1 2)) -> '(0 2 2 4);;(define (replace-nth-zero lst n val)  (let ([i (index-of-nth-zero lst n)])    (append (take lst i) (cons val (drop lst (add1 i)))))) ;; There's a 90% chance that a new tile will be a two; 10% a four.;;(define (new-tile)  (if (> (random) 0.9) 4 2)) ;; Create a random initial game-board with two non-zeros (2 or 4) ;; and the rest 0s.;;;; E.g. '(0 0 0 0  ;;        0 2 0 0  ;;        2 0 0 0  ;;        0 0 0 0);;(define (initial-state [side *side*])  (shuffle (append (list (new-tile) (new-tile))                   (make-list (- (sqr side) 2) 0)))) ;; The game finishes when no matter which way you slide, the board doesn't;; change.;;(define (finished? state [n *side*])  (let ([grid (chop state n)])    (for/and ([op (list left right up down)])      (equal? grid (op grid))))) ;;--------------------------------------------------------------------;; Graphics;;(define *text-size* 30)(define *max-text-width* 40)(define *tile-side* 50)(define *grid-spacing* 5)(define *grid-side* (+ (* *side* *tile-side*)                       (* (add1 *side*) *grid-spacing*))) ;; Memoization - caching images takes the strain off the gc;;(define-syntax define-memoized  (syntax-rules ()    [(_ (f args ...) bodies ...)     (define f       (let ([results (make-hash)])         (lambda (args ...)           ((λ vals              (when (not (hash-has-key? results vals))                (hash-set! results vals (begin bodies ...)))              (hash-ref results vals))            args ...))))])) ;; Look-up the (i,j)th element in the flat representation.;;(define (square/ij state i j)  (list-ref state (+ (* *side* i) j))) ;; Linear interpolation between a and b:;;;;   (interpolate 0.0 a b) -> a;;   (interpolate 1.0 a b) -> b;;(define (interpolate k a b)  (+ (* (- 1 k) a)     (* k b))) ;; Key value lookup with default return - is there an out-of-the-box function;; for this?;;(define (lookup key lst default)  (let ([value (assoc key lst)])    (if value (second value) default)))  ;; Make a tile without a number on it in the appropriate color.;;(define (plain-tile n)  (square *tile-side*           'solid           (lookup n *tile-bg-colors* *default-tile-bg-color*))) ;; Make text for a tile;;(define (tile-text n)  (let* ([t (text (lookup n *text* (number->string n))                  *text-size*                  (lookup n *tile-fg-colors* *default-tile-fg-color*))]         [side (max (image-width t) (image-height t))])    (scale (if (> side *max-text-width*) (/ *max-text-width* side) 1) t))) (define-memoized (make-tile n)  (overlay   (tile-text n)   (plain-tile n))) ;; Place a tile on an image of the grid at (i,j);;(define (place-tile/ij tile i j grid-image)  (define (pos k)    (+ (* (add1 k) *grid-spacing*)       (* k *tile-side*)))  (underlay/xy grid-image (pos j) (pos i) tile)) ;; Make an image of the grid from the flat representation;;(define *last-state* null) ; Cache the previous grid to avoid(define *last-grid* null)  ; senseless regeneration (define (state->image state)  (unless (equal? state *last-state*)    (set! *last-grid*          (for*/fold ([im (square *grid-side* 'solid *grid-color*)])            ([i (in-range *side*)]             [j (in-range *side*)])            (place-tile/ij (make-tile (square/ij state i j))                           i j                           im)))    (set! *last-state* state))  *last-grid*) (define *empty-grid-image*  (state->image (make-list (sqr *side*) 0))) ;; Convert the sparse representation of moves into a single frame in an ;; animation at time k, where k is between 0.0 (start state) and 1.0;; (final state).;;(define (moves->frame moves k)  (for*/fold ([grid *empty-grid-image*])    ([m moves])    (match-define (list value (list i1 j1) (list i2 j2)) m)    (place-tile/ij (make-tile value)                   (interpolate k i1 i2) (interpolate k j1 j2)                   grid))) ;; Animation of simultaneously moving tiles.;;(define (animate-moving-tiles state op)  (let ([grid (chop state)])    (build-list 9 (λ (i)                     (λ ()                       (moves->frame (op grid)                                     (* 0.1 (add1 i)))))))) ;; Animation of a tile appearing in a previously blank square.;;(define (animate-appearing-tile state value index)  (let ([start (state->image state)]        [tile (make-tile value)]        [i (quotient index *side*)]        [j (remainder index *side*)])    (build-list 4 (λ (m)                     (λ ()                       (place-tile/ij (overlay                                       (scale (* 0.2 (add1 m)) tile)                                      (plain-tile 0))                                     i j                                     start)))))) ;;--------------------------------------------------------------;;;; The Game;; ;; an image-procedure is a procedure of no arguments that produces an image ;; a world contains:;; state is a ?;; score is a number;; winning-total is #f or a number, representing the final score <-- is this;;  necessary?;; frames is a (list-of image-procedure);; start-time is a number, in seconds(define-struct world (state score winning-total frames start-time) #:transparent) ;; The game is over when any animations have been finished and ;; no more moves are possible.;;;; note that winning the game does *not* end the game.;;(define (game-over? w)  (match-define (world state score wt frames start-time) w)  (and (null? frames) ; Finish animations to reach final state and show the banner       (or (finished? state)           (out-of-time? (world-start-time w))))) ;; Is the player out of time?(define (out-of-time? start-time)  (and *time-limit* (< (+ start-time *time-limit*) (current-seconds)))) ;; Given an arrow key return the operations to change the state and;; produce the sliding animation.;;(define (key->ops a-key)  (cond    [(key=? a-key "left")  (list left moves-grid-left)]    [(key=? a-key "right") (list right moves-grid-right)]    [(key=? a-key "up")    (list up moves-grid-up)]    [(key=? a-key "down")  (list down moves-grid-down)]    [else (list #f #f)])) ;; Respond to a key-press;;(define (change w a-key)  (match-let ([(list op moves-op) (key->ops a-key)]              [(world st score wt frames start-time) w])    (cond [(out-of-time? start-time) w] ; Stop accepting key-presses           [op           (let* ([grid (chop st)]                  [slide-state (flatten (op grid))])             (if (equal? slide-state st)                 w                       ; sliding had no effect                 (let* ([replace (random (count-zeros slide-state))]                        [index (index-of-nth-zero slide-state replace)]                        [value (new-tile)]                        [new-state (replace-nth-zero slide-state replace value)]                        [horizontal? (member a-key (list "left" "right"))])                   (make-world new-state                               (+ score (score-increment                                          (if horizontal? grid (transpose grid))))                               (cond [wt wt]                                     [(won-game? new-state)                                       (apply + (flatten new-state))]                                     [else #f])                               (append frames                                       (animate-moving-tiles st moves-op)                                       (animate-appearing-tile slide-state value index))                               start-time))))]          [(key=? a-key " ")             ; rotate the board           (make-world ((compose flatten transpose reverse) (chop st))                       score wt                       (append frames                               (animate-moving-tiles st moves-grid-rotate))                       start-time)]           [else w])))                    ; unrecognised key - no effect ;; Are we there yet?;;(define (won-game? state)  (= (apply max state) *tile-that-wins*)) ;; Banner overlay text: e.g. You won! / Game Over, etc.;;(define (banner txt state [color 'black])  (let ([b-text (text txt 30 color)])    (overlay     b-text     (rectangle (* 1.2 (image-width b-text))                (* 1.4 (image-height b-text))                'solid 'white)     (state->image state)))) ;; Convert number of seconds to "h:mm:ss" or "m:ss" format;;(define (number->time-string s)  (define hrs (quotient s 3600))  (define mins (quotient (remainder s 3600) 60))  (define secs (remainder s 60))  (define (xx n)    (cond [(<= n 0) "00"]          [(<= n 9) (format "0~a" n)]          [else (remainder n 60)]))  (if (>= s 3600)      (format "~a:~a:~a" hrs (xx mins) (xx secs))      (format "~a:~a" mins (xx secs)))) (define (time-remaining start)  (+ *time-limit* start (- (current-seconds)))) (define (time-elapsed start)  (- (current-seconds) start)) ;; Display the grid with score below.;;;; If there are frames, show the next one. Otherwise show the steady state.;;(define (show-world w)  (match-define (world state score wt frames start-time) w)  (let* ([board (if (null? frames)                    (cond [(finished? state) (banner "Game over" state)]                          [(out-of-time? start-time) (banner "Out of Time" state 'red)]                           ;; Q: Why wt (i.e. winning-total) rather than won-game?                           ;; A: wt allows the keen player to continue playing...                          [(equal? (apply + (flatten state)) wt) (banner "You won!" state)]                          [else (state->image state)])                    ((first frames)))]         [score-text (text (format "Score: ~a" score) 16 'dimgray)]         [seconds ((if *time-limit* time-remaining time-elapsed) start-time)]         [time-text (text (format "Time: ~a"                                   (number->time-string seconds))                           16                          (cond [(or (> seconds *amber-alert*) (not *time-limit*)) 'gray]                                [(> seconds *red-alert*) 'orange]                                 [else 'red]))])            (scale *magnification*           (above            board            (rectangle 0 5 'solid 'white)            (beside             score-text             (rectangle (- (image-width board)                           (image-width score-text)                           (image-width time-text)) 0 'solid 'white)             time-text))))) ;; Move to the next frame in the animation.;;(define (advance-frame w)  (match-define (world state score wt frames start-time) w)  (if (null? frames)      w      (make-world state score wt (rest frames) start-time))) ;; Use this state to preview the appearance of all the tiles;;(define (all-tiles-state)  (let ([all-tiles '(0 2 4 8 16 32 64 128 256 512 1024 2048 4096)])    (append all-tiles (make-list (- (sqr *side*) (length all-tiles)) 0)))) ;; The event loop;;(define (start)  (big-bang (make-world (initial-state)                         ;(all-tiles-state)                         0 #f null (current-seconds))            (to-draw show-world)            (on-key change)            (on-tick advance-frame 0.01)            (stop-when game-over? show-world)            (name "2048 - Racket edition"))) ;; ;; TESTS;;(module+ test  (set-side! 4)   (check-equal? (slide-left '(0 0 0 0)) '(0 0 0 0))  (check-equal? (slide-left '(1 2 3 4)) '(1 2 3 4))  (check-equal? (slide-left '(2 0 4 0)) '(2 4 0 0))  (check-equal? (slide-left '(0 0 2 4)) '(2 4 0 0))  (check-equal? (slide-left '(2 0 2 0)) '(4 0 0 0))  (check-equal? (slide-left '(0 8 8 0)) '(16 0 0 0))  (check-equal? (slide-left '(4 4 8 8)) '(8 16 0 0))  (check-equal? (slide-right '(4 4 8 8)) '(0 0 8 16))  (check-equal? (slide-right '(4 4 4 0)) '(0 0 4 8))   (check-equal? (moves-row-left '(0 0 0 0)) '())  (check-equal? (moves-row-left '(1 2 3 4))                 '((1 0 0)                  (2 1 1)                  (3 2 2)                  (4 3 3)))   (check-equal? (moves-row-left '(2 0 4 0)) '((2 0 0)                                              (4 2 1)))   (check-equal? (moves-row-right '(2 0 4 0)) '((4 2 3)                                               (2 0 2)))   (check-equal? (moves-row-left '(0 0 2 4)) '((2 2 0)                                              (4 3 1)))   (check-equal? (moves-row-left '(2 0 2 0)) '((2 0 0)                                              (2 2 0)))   (check-equal? (moves-row-left '(2 2 2 0)) '((2 0 0)                                              (2 1 0)                                              (2 2 1)))   (check-equal? (moves-row-right '(2 2 2 0)) '((2 2 3)                                               (2 1 3)                                               (2 0 2)))   (check-equal? (moves-row-left '(2 2 4 4)) '((2 0 0)                                              (2 1 0)                                              (4 2 1)                                              (4 3 1)))   (check-equal? (moves-row-right '(2 2 4 4)) '((4 3 3)                                               (4 2 3)                                               (2 1 2)                                               (2 0 2)))   (check-equal? (add-row-coord 7 '((2 0 0)                                   (2 1 0)                                   (4 2 1)))                '((2 (7 0) (7 0))                  (2 (7 1) (7 0))                  (4 (7 2) (7 1))))   (check-equal? (left '(( 0 8 8 0)                        (16 0 0 0)                        ( 2 2 4 4)                        ( 0 2 2 2)))                '((16 0 0 0)                  (16 0 0 0)                  ( 4 8 0 0)                  ( 4 2 0 0)))  (check-equal? (right '(( 0 8 8 0)                         (16 0 0 0)                         ( 2 2 4 4)                         ( 0 2 2 2)))                '((0 0 0 16)                  (0 0 0 16)                  (0 0 4  8)                  (0 0 2  4)))  (check-equal? (up '((0 16 2 0)                       (8  0 2 2)                       (8  0 4 2)                       (0  0 4 2)))                '((16 16 4 4)                   (0  0  8 2)                   (0  0  0 0)                   (0  0  0 0)))  (check-equal? (down '((0 16 2 0)                         (8  0 2 2)                         (8  0 4 2)                         (0  0 4 2)))                '((0  0  0 0)                   (0  0  0 0)                   (0  0  4 2)                   (16 16 8 4)))   (check-equal? (left '(( 0 8 8 0)                        (16 0 0 0)                        ( 2 2 4 4)                        ( 0 2 2 2)))                '((16 0 0 0)                  (16 0 0 0)                  ( 4 8 0 0)                  ( 4 2 0 0)))   (check-equal? (moves-grid-left '(( 0 8 8 0)                                   (16 0 0 0)                                   ( 2 2 4 4)                                   ( 0 2 2 2)))                '((8  (0 1) (0 0))                  (8  (0 2) (0 0))                  (16 (1 0) (1 0))                  (2  (2 0) (2 0))                  (2  (2 1) (2 0))                  (4  (2 2) (2 1))                  (4  (2 3) (2 1))                  (2  (3 1) (3 0))                  (2  (3 2) (3 0))                  (2  (3 3) (3 1))))   (check-equal? (moves-grid-right '(( 0 8 8 0)                                    (16 0 0 0)                                    ( 2 2 4 4)                                    ( 0 2 2 2)))                '((8  (0 2) (0 3))                  (8  (0 1) (0 3))                  (16 (1 0) (1 3))                  (4  (2 3) (2 3))                  (4  (2 2) (2 3))                  (2  (2 1) (2 2))                  (2  (2 0) (2 2))                  (2  (3 3) (3 3))                  (2  (3 2) (3 3))                  (2  (3 1) (3 2))))    (check-equal? (moves-grid-up '(( 0 8 8 0)                                 (16 0 0 0)                                 ( 2 2 4 4)                                 ( 0 2 2 2)))                '((16 (1 0) (0 0))                  (2  (2 0) (1 0))                  (8  (0 1) (0 1))                  (2  (2 1) (1 1))                  (2  (3 1) (1 1))                  (8  (0 2) (0 2))                  (4  (2 2) (1 2))                  (2  (3 2) (2 2))                  (4  (2 3) (0 3))                  (2  (3 3) (1 3))))   (check-equal? (moves-grid-down '(( 0 8 8 0)                                   (16 0 0 0)                                   ( 2 2 4 4)                                   ( 0 2 2 2)))                '((2  (2 0) (3 0))                  (16 (1 0) (2 0))                  (2  (3 1) (3 1))                  (2  (2 1) (3 1))                  (8  (0 1) (2 1))                  (2  (3 2) (3 2))                  (4  (2 2) (2 2))                  (8  (0 2) (1 2))                  (2  (3 3) (3 3))                  (4  (2 3) (2 3))))    (check-equal? (chop '(1 2 3 4 5 6 7 8) 4)                '((1 2 3 4) (5 6 7 8)))   (check-equal? (length (initial-state 5)) 25)   (let* ([initial (initial-state)]         [initial-sum (apply + initial)]         [largest-3 (take (sort initial >) 3)])    (check-equal? (length initial) 16)    (check-true (or (= initial-sum 4)                    (= initial-sum 6)                    (= initial-sum 8)))    (check-true (or (equal? largest-3  '(2 2 0))                    (equal? largest-3  '(4 2 0))                    (equal? largest-3  '(4 4 0)))))   (check-equal? (count-zeros '(1 0 1 0 0 0 1)) 4)  (check-equal? (count-zeros '(1 1)) 0)  (check-equal? (replace-nth-zero '(0 0 0 1 2 0) 2 5)                '(0 0 5 1 2 0))   (check-true (finished? '(1 2 3 4) 2))  (check-false (finished? '(2 2 3 4) 2))) (start) `

## REXX

This REXX version has the features:

•   allows specification of N,   the size of the grid   (default is 4).
•   allows specification of the winning number   (default is 2048)
•   allows specification for the random BIF's seed   (no default).
•   allows abbreviations for the directions   (Up, Down, Left, Right).
•   allows the player to quit the game at any time.
•   does error checking/validation for entered directions   (in response to the prompt).
•   keeps track of the number of legal moves made and the score.
•   displays the number of moves and the score   (when a blank is entered).
•   displays an error message if a move doesn't do anything.
•   displays a message if a winning move was entered.
•   displays the game board as a grid   (with boxes).
`/*REXX program lets a user play the  2048  game on an  NxN  grid  (default is 4x4 grid).*/parse arg N win seed .                           /*obtain optional arguments from the CL*/if   N=='' |   N==","  then    N=    4           /*Not specified?  Then use the default.*/if win=='' | win==","  then  win=2**11           /* "      "         "   "   "      "   */if datatype(seed, 'W') then call random ,,seed   /*Specified?  Then use seed for RANDOM.*/L=length(win) + 2                                /*L:  used for displaying the grid #'s.*/eye=copies("─", 8);  pad=left('', length(eye)+2) /*eye-catchers; and perusable perusing.*/b= ' '                                           /*comfortable readable name for a blank*/prompt= eye "Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:"move=1;      moves=0;      score=0;      ok=1    /*simulation that a move was performed.*/@.=b                                             /*define all grid elements to a blank. */     do  until any(win);  if ok  then call put;   ok=1;   say;   call showGrid     say;  say prompt;  parse pull a x . 1 d 2 1 way xx;  upper d a x     if a==''  then do;      ok=0                /*the user entered blank(s) or nothing.*/                    say copies(eye,5)    'moves:'      moves     eye     "score:"    score                    iterate                      /* [↑]  display # of moves & the score.*/                    end     if x\==''               then call err  "too many arguments entered: "   xx     if abbrev('QUIT',a,1)   then do;  say;  say eye  "quitting the game".;  exit 1;   end     good=abbrev('UP',a,1) | abbrev('DOWN',a,1) | abbrev('RIGHT',a,1) | abbrev('LEFT',a,1)     if \good                then call err  "invalid direction: "       way     if \ok  then  iterate;       moves=moves + 1;                 call mov     end   /*until*/saysay translate(eye  "Congrats!!  You've won the"  win  'game!' eye,"═",'─')  "score:" scoreexit 0                                           /*stick a fork in it,  we're all done. *//*──────────────────────────────────────────────────────────────────────────────────────*/showGrid:     do    r=0  for N+2;    _= '║';             __= '╠'                 do c=1  for N;      _=_ || row()'║';    __=__ || copies("═", L)'╬'                 end   /*c*/              if r==0  then _= '╔'translate(substr(_, 2, length(_)-2),  "╦",  '║')"╗"              if r >N  then _= '╚'translate(substr(_, 2, length(_)-2),  "╩",  '║')"╝"                                 say pad _              if r<N & r>0  then say pad substr(__, 1, length(__) -1)"╣"              end      /*r*/;        return/*──────────────────────────────────────────────────────────────────────────────────────*/@:   procedure expose @.;  parse arg row,col;                return @.row.colany: arg ?; do r=1  for N; do c=1  for N;  if @.r.c==?  then return 1; end; end;  return 0err: say;   say eye  '***error*** '   arg(1);       say;                  ok=0;   returno_c: \$=;  do k=1  for N; \$=\$ word(@.k.c .,1); end;  !=space(translate(\$,,.))==''; return \$o_r: \$=;  do k=1  for N; \$=\$ word(@.r.k .,1); end;  !=space(translate(\$,,.))==''; return \$put: if \any(b) then call err ,"game over, no more moves."; if move then call two; returnrow: if r==0 | r>N  then return copies('═', L);                    return center(@.r.c, L)ten: if random(9)==4  then return 4;  return 2    /*10% of the time, put 4 instead of 2.*/two: do  until @.p.q==b;  p=random(1,N);  q=random(1,N); end;      @.p.q=ten();   return/*──────────────────────────────────────────────────────────────────────────────────────*/mov: move=0;      if d=='R'  then call moveLR N, 1, -1   /*move (slide) numbers  right. */                  if d=='L'  then call moveLR 1, N, +1   /*  "     "       "      left. */                  if d=='U'  then call moveUD 1, N, +1   /*  "     "       "        up. */                  if d=='D'  then call moveUD N, 1, -1   /*  "     "       "      down. */     if \move  then call err 'moving '    way    " doesn't change anything.";       return/*──────────────────────────────────────────────────────────────────────────────────────*/moveLR: parse arg start, sTo, #          do   r=1  for N;    old=o_r();    if !  then iterate    /*is this row blank?  */            do N-1;           call packLR                         /*pack  left or right.*/            end        /*N-1*/                                    /* [↓]  get new tiles.*/          new=o_r();             move= move | (old\==new)         /*indicate tiles moved*/              do c=start  for N-1  by #  while @.r.c\==b          /*slide left or right.*/              if @.r.c\[email protected](r,c+#)  then iterate                   /*not a duplicate ?   */              @.r.[email protected].r.c * 2;  score=score + @.r.c               /*double;  bump score */              c=c + #        ;  @.r.c=b;    move=1                /*bump C; blank dup 2.*/              end      /*c*/                                      /* [↑]  indicate move.*/          call packLR                                             /*pack  left or right.*/          end          /*r*/;                             return/*──────────────────────────────────────────────────────────────────────────────────────*/moveUD: parse arg start, Sto, #          do   c=1  for N;    old=o_c();    if !  then iterate    /*is this col blank?  */            do N-1;           call packUD                         /*pack  up or down.   */            end        /*N-1*/                                    /* [↓]  get new tiles.*/          new=o_c();             move= move | (old\==new)         /*indicate tiles moved*/              do r=start  for N-1  by #  while @.r.c\==b          /*slide up or down.   */              if @.r.c\[email protected](r+#,c)  then iterate                   /*not a duplicate ?   */              @.r.[email protected].r.c * 2;  score=score + @.r.c               /*double;  bump score */              r=r + #        ;  @.r.c=b;    move=1                /*bump R; blank dup 2.*/              end      /*r*/                                      /* [↑]  indicate move.*/          call packUD                                             /*pack  up or down.   */          end          /*c*/;                             return/*──────────────────────────────────────────────────────────────────────────────────────*/packLR:       do c=start  for N-1  by #                           /*slide left or right.*/              if @.r.c\==b  then iterate                          /*Not a blank?  Skip. */                 do s=c  to sTo  by #;      @.r.[email protected](r, s+#)       /*slide left or right.*/                 end   /*s*/;               @.r.sTo=b             /*handle the last one.*/              end      /*c*/;               return/*──────────────────────────────────────────────────────────────────────────────────────*/packUD:       do r=start  for N-1  by #                           /*slide up or down.   */              if @.r.c\==b  then iterate                          /*Not a blank?  Skip. */                 do s=r  to sTo  by #;      @.s.[email protected](s+#, c)       /*slide up or down.   */                 end   /*s*/;               @.sTo.c=b             /*handle the last one.*/              end      /*r*/;               return`
output   when using the default inputs:
```           ╔══════╦══════╦══════╦══════╗
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  2   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
right                                         ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║  2   ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║  2   ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
up                                            ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║      ║      ║      ║  4   ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║  2   ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
◄■■■■■■■■■■■■■ user input (a blank)
──────────────────────────────────────── moves: 2 ──────── score: 4

╔══════╦══════╦══════╦══════╗
║      ║      ║      ║  4   ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║  2   ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
left                                          ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║  4   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  2   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║  2   ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
l                                             ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║  4   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  2   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  2   ║  4   ║      ║      ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
dow                                           ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║      ║      ║      ║  2   ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  4   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  4   ║  4   ║      ║      ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
left                                          ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║  2   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║      ║      ║  2   ║
╠══════╬══════╬══════╬══════╣
║  4   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  8   ║      ║      ║      ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
lef                                           ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║  2   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  2   ║      ║      ║  2   ║
╠══════╬══════╬══════╬══════╣
║  4   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  8   ║      ║      ║      ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
d                                             ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  4   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  4   ║      ║      ║  2   ║
╠══════╬══════╬══════╬══════╣
║  8   ║      ║      ║  2   ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
d                                             ◄■■■■■■■■■■■■■ user input

╔══════╦══════╦══════╦══════╗
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║  2   ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  8   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  8   ║      ║      ║  4   ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
◄■■■■■■■■■■■■■ user input (a blank)
──────────────────────────────────────── moves: 9 ──────── score: 32

╔══════╦══════╦══════╦══════╗
║      ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║      ║  2   ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  8   ║      ║      ║      ║
╠══════╬══════╬══════╬══════╣
║  8   ║      ║      ║  4   ║
╚══════╩══════╩══════╩══════╝

──────── Please enter a direction  (Up, Down, Right, Left)       ───or───    Quit:
q                                             ◄■■■■■■■■■■■■■ user input

──────── quitting the game
```

## Ring

` # Project : 2048 Game load "stdlib.ring"load "guilib.ring" C_GAMETITLE		= '2048 Game'C_WINDOWBACKGROUND 	= "background-color: gray;"if isMobile()	C_LABELFONTSIZE 	= "font-size:120px;"	C_BUTTONFONTSIZE 	= "font-size:160px;"else 	C_LABELFONTSIZE 	= "font-size:50px;"	C_BUTTONFONTSIZE 	= "font-size:80px;"okC_PLAYERSCORESTYLE	= "color:white;background-color:rgb(50,50,50);border-radius:17px;" + C_LABELFONTSIZEC_NEWGAMESTYLE		= 'color:white;background-color:rgb(50,50,50);border-radius:17px;' + C_LABELFONTSIZEC_EMPTYBUTTONSTYLE 	= 'border-radius:17px;background-color:silver;' + C_BUTTONFONTSIZEC_BUTTON2STYLE 		= 'border-radius:17px;color:black; background-color: yellow ;' + C_BUTTONFONTSIZEC_BUTTON4STYLE 		= 'border-radius:17px;color:black; background-color: violet ;' + C_BUTTONFONTSIZEC_BUTTON8STYLE 		= 'border-radius:17px;color:white; background-color: purple ;' + C_BUTTONFONTSIZEC_BUTTON16STYLE 	= 'border-radius:17px;color:white; background-color: blue ;' + C_BUTTONFONTSIZEC_BUTTON32STYLE 	= 'border-radius:17px;color:white; background-color: red ;' + C_BUTTONFONTSIZEC_BUTTON64STYLE 	= 'border-radius:17px;color:black; background-color: lightgray ;' + C_BUTTONFONTSIZEC_BUTTON128STYLE 	= 'border-radius:17px;color:black; background-color: white ;' + C_BUTTONFONTSIZEC_BUTTON256STYLE 	= 'border-radius:17px;color:white; background-color: black ;' + C_BUTTONFONTSIZEC_BUTTON512STYLE 	= 'border-radius:17px;color:white; background-color: Purple ;' + C_BUTTONFONTSIZEC_BUTTON1024STYLE 	= 'border-radius:17px;color:black; background-color: Yellow ;' + C_BUTTONFONTSIZEC_BUTTON2048STYLE 	= 'border-radius:17px;color:white; background-color: Green ;' + C_BUTTONFONTSIZEC_LAYOUTSPACING		= 10C_PLAYERSCORE		= 'Player Score :  ' size = 4limit = 2num = 0flag = 0x1 = 0x2 = 0y1 = 0y2 = 0nScore = 0button = newlist(size,size)buttonsave = newlist(size,size)LayoutButtonRow = list(size+2)moveleft = []moveright = []moveup = []movedown = []myfilter2 = nullmyfilter3 = nullwinheight = 0winwidth = 0 app = new qApp {          StyleFusion()          processevents()          win = new qWidget() {                  setWindowTitle(C_GAMETITLE)                  setgeometry(100,100,600,700)                  setminimumwidth(300)                  setminimumheight(300)		  if not isMobile()	                  grabkeyboard()		  ok                  setstylesheet(C_WINDOWBACKGROUND)                  move(490,100)                   for n = 1 to size                       for m = 1 to size                            button[n][m] = new MyButton(win)                       next                  next                  newgame = new qLabel(win)                  playerscore = new qLabel(win)                  myfilter3 = new qAllEvents(win) {                  setMouseButtonPressEvent("pPress()")                  setMouseButtonReleaseEvent("pRelease()")}                  installeventfilter(myfilter3)                  myfilter2 = new qAllEvents(win) {                  setkeypressevent("keypress()") }                  installeventfilter(myfilter2)                  winwidth = win.width()                  winheight = win.height()                  for n = 1 to size + 2			LayoutButtonRow[n] = new QHBoxLayout() {				setSpacing(C_LAYOUTSPACING) 			}                  next                  for n = 1 to size                       for m = 1 to size                            button[n][m] { temp = text() }                            buttonsave[n][m] = temp                            button[n][m] = new MyButton(win) {                                                   setalignment(Qt_AlignHCenter | Qt_AlignVCenter)                                                   setstylesheet(C_EMPTYBUTTONSTYLE)                                                   show()                                           }                       next                  next                  for n = 1 to size                       for m = 1 to size                            LayoutButtonRow[n].AddWidget(button[m][n])                            win.show()                            temp = buttonsave[n][m]                            button[n][m].settext(temp)                       next  		       LayoutButtonRow[n].setSpacing(C_LAYOUTSPACING)                  next                  playerscore {                                    setGeometry(0,4*floor(winheight/6),winwidth,floor(winheight/6))                                    setalignment(Qt_AlignHCenter | Qt_AlignVCenter)                                    settext(C_PLAYERSCORE + nScore)				    setStylesheet(C_PLAYERSCORESTYLE)                                    show()		  }                  newgame  {                                  setGeometry(0,5*floor(winheight/6),winwidth,floor(winheight/6))                                  setalignment(Qt_AlignHCenter | Qt_AlignVCenter)                                  setstylesheet(C_NEWGAMESTYLE)                                  settext('New Game')                                  myfilter4 = new qallevents(newgame)                                  myfilter4.setMouseButtonPressEvent("pbegin()")                                  installeventfilter(myfilter4)                                  show()                                  }                  LayoutButtonRow[size+1].AddWidget(playerscore)                  LayoutButtonRow[size+2].AddWidget(newgame)                  LayoutButtonMain = new QVBoxLayout() {			setSpacing(C_LAYOUTSPACING)			for n = 1 to size+2				AddLayout(LayoutButtonRow[n])				win.show()			next		  }		  win.setLayout(LayoutButtonMain)                  win.show()                  pbegin()                    show()         }    exec()} func pPress()        x1 = myfilter3.getglobalx()        y1 = myfilter3.getglobaly() func pRelease()        x2 = myfilter3.getglobalx()        y2 = myfilter3.getglobaly()        difx = x2 - x1	dify = y2 - y1	if fabs(difx) > fabs(dify)		if difx < 0			pleft()		else 			pRight()		ok	else 		if dify < 0			pUp()		else 			pDown()		ok	ok func keypress()         nKey = myfilter2.getkeycode()         switch nKey                   on 16777234 pleft()                   on 16777236 pright()                   on 16777235 pup()                   on 16777237 pdown()        off func pbegin()       numbers = [['2','2'],['2','4']]       randnew = newlist(2,2)       for n = 1 to size            for m = 1 to size                 button[n][m].setStylesheet(C_EMPTYBUTTONSTYLE)                 button[n][m].settext('')            next        next        while true                rn1 = random(size - 1) + 1                rm1 = random(size - 1) + 1                rn2 = random(size - 1) + 1                rm2 = random(size - 1) + 1                bool = (rn1 = rn2) and (rm1 = rm2)                if not bool                   exit                ok        end        rand = random(limit - 1) + 1        button[rn1][rm1].settext(numbers[rand][1])        button[rn2][rm2].settext(numbers[rand][2])        nScore = 0        playerscore.settext(C_PLAYERSCORE) func pMoveInDirection cFunc         num = gameover()        if num = size*size           flag = 1           msgBox('You lost!')           pbegin()        ok        if flag = 0           call cFunc()           sleep(0.5)           newnum()        ok func pdown()	pMoveInDirection(:pMoveDown) func pup()	pMoveInDirection(:pMoveUp) func pleft()	pMoveInDirection(:pMoveLeft) func pright()	pMoveInDirection(:pMoveRight) func pmoveleft()       for n = 1 to size            moveleft = []            for m = 1 to size                 button[m][n] {temp = text()}                 if temp != ''                    add(moveleft,temp)                 ok            next            movetilesleft(n,moveleft)       next func pmoveright()       for n = 1 to size            moveright = []            for m = size to 1 step -1                 button[m][n] {temp = text()}                 if temp != ''                    add(moveright,temp)                 ok            next            movetilesright(n,moveright)        next        return func pmoveup()       for n = 1 to size            moveup = []            for m = 1 to size                 button[n][m] {temp = text()}                 if temp != ''                    add(moveup,temp)                 ok            next            movetilesup(n,moveup)        next        return func pmovedown()       for n = 1 to size            movedown = []            for m = size to 1 step -1                 button[n][m] {temp = text()}                 if temp != ''                    add(movedown,temp)                 ok            next            movetilesdown(n,movedown)        next        return func movetilesleft(nr,moveleft)       for p = 1 to len(moveleft) - 1            temp1 = moveleft[p]            temp2 = moveleft[p+1]            temp = string(number(temp1) + number(temp2))            if (temp1 = temp2) and (temp1 != '0') and (temp2 != '0') and (temp1 != '') and (temp2 != '')               if temp != '0' and temp != ''                  nScore = nScore + temp                  playerscore.settext(C_PLAYERSCORE + nScore)                  flag = 1                  moveleft[p] = temp                  del(moveleft,p+1)               ok            ok       next       for n = 1 to len(moveleft)            button[n][nr].settext(moveleft[n])       next       for n = len(moveleft) + 1 to size             if n <= size               button[n][nr].setStylesheet(C_EMPTYBUTTONSTYLE)               button[n][nr].settext('')            ok       next       return func movetilesright(nr,moveright)       flag = 0       for p = 2 to len(moveright)            temp1 = moveright[p]            temp2 = moveright[p-1]             if (temp1 = temp2) and (temp1 != '0') and (temp2 != '0') and (temp1 != '') and (temp2 != '')               temp = string(number(temp1) + number(temp2))               if temp != '0' and temp != ''                  nScore = nScore + temp                  playerscore.settext(C_PLAYERSCORE + nScore)                  flag = 1                  moveright[p] = temp                  del(moveright,p-1)               ok            ok       next       for n = 1 to len(moveright)            button[size-n+1][nr].settext(moveright[n])       next       for n = 1 to size - len(moveright)            if n <= size               button[n][nr].setStylesheet(C_EMPTYBUTTONSTYLE)               button[n][nr].settext('')            ok       next  func movetilesup(nr,moveup)        flag = 0        for p = 1 to len(moveup) - 1             temp1 = moveup[p]              temp2 = moveup[p+1]             if (temp1 = temp2) and (temp1 != '0') and (temp2 != '0') and (temp1 != '') and (temp2 != '')               temp = string(number(temp1) + number(temp2))               if temp != '0' and temp != ''                  nScore = nScore + temp                  playerscore.settext(C_PLAYERSCORE + nScore)                  flag = 1                  moveup[p] = temp                  del(moveup,p+1)               ok            ok       next       for n = 1 to len(moveup)            button[nr][n].settext(moveup[n])       next       for n = len(moveup) + 1 to size             if n <= size               button[nr][n].setStylesheet(C_EMPTYBUTTONSTYLE)               button[nr][n].settext('')            ok       next func movetilesdown(nr,movedown)        flag = 0        for p = 1 to len(movedown) - 1             temp1 = movedown[p]             temp2 = movedown[p+1]             if (temp1 = temp2) and (temp1 != '0') and (temp2 != '0') and (temp1 != '') and (temp2 != '')               temp = string(number(temp1) + number(temp2))               if temp != '0' and temp != ''                  nScore = nScore + temp                  playerscore.settext(C_PLAYERSCORE + nScore)                  flag = 1                  movedown[p] = temp                  del(movedown,p+1)               ok            ok       next       for n = 1 to len(movedown)            button[nr][size-n+1].settext(movedown[n])       next       for n = size - len(movedown) to 1 step -1             if n <= size               button[nr][n].setStylesheet(C_EMPTYBUTTONSTYLE)               app.processevents()               button[nr][n].settext('')            ok       next func newnum()        while true                rn = random(size - 1) + 1                rm = random(size - 1) + 1                if button[rn][rm].text() = ''                   button[rn][rm].settext('2')                   exit                ok        end        return func gameover()        num = 0        flag = 0        for n = 1 to size             for m = 1 to size                   if button[n][m].text() != ''                     num = num + 1                  ok              next        next        return num func msgBox(text) {	m = new qMessageBox(win) {	       setWindowTitle('2048 Game')	       setText(text)	       show()	       }        } func showarray(vect)        see "["        svect = ""        for n = 1 to len(vect)              svect = svect + vect[n] + " "        next        svect = left(svect, len(svect) - 1)        see svect        see "]" + nl class MyButton from qLabel       func setText(cValue)              Super.setText(cValue)              switch cValue                         on '2' 		setStyleSheet(C_BUTTON2STYLE)                        on '4' 		setStylesheet(C_BUTTON4STYLE)                        on '8' 		setStylesheet(C_BUTTON8STYLE)                        on '16' 	setStylesheet(C_BUTTON16STYLE)                        on '32' 	setStylesheet(C_BUTTON32STYLE)                        on '64' 	setStylesheet(C_BUTTON64STYLE)                        on '128' 	setStylesheet(C_BUTTON128STYLE)			on '256'	setStylesheet(C_BUTTON256STYLE)			on '512'	setStylesheet(C_BUTTON512STYLE)			on '1024'	setStylesheet(C_BUTTON1024STYLE)			on '2048'	setStylesheet(C_BUTTON2048STYLE)              off `

## Ruby

inspired by the Perl6 version

` #!/usr/bin/ruby require 'io/console' class Board  def initialize size=4, win_limit=2048, cell_width = 6    @size = size; @cw = cell_width; @win_limit = win_limit    @board = Array.new(size) {Array.new(size, 0)}    @moved = true; @score = 0; @no_more_moves = false    spawn  end   def draw    print "\n\n" if @r_vert    print '    ' if @r_hori    print '┌' + (['─' * @cw] * @size).join('┬')  + '┐'    @board.each do |row|      print "\n"      formated = row.map {|num| num == 0 ? ' ' * @cw : format(num)}      print '    ' if @r_hori      puts '│' + formated.join('│') + '│'      print '    ' if @r_hori      print '├' + ([' '  * @cw] * @size).join('┼') + '┤'    end    print "\r"    print '    ' if @r_hori    puts '└' + (['─' * @cw] * @size).join('┴')  + '┘'  end   def move direction    case direction    when :up      @board = column_map {|c| logic(c)}      @r_vert = false if \$rumble    when :down      @board = column_map {|c| logic(c.reverse).reverse}       @r_vert = true if \$rumble    when :left       @board = row_map {|r| logic(r)}      @r_hori = false if \$rumble    when :right      @board = row_map {|r| logic(r.reverse).reverse}       @r_hori = true if \$rumble    end    spawn    @moved = false  end   def print_score    puts "Your Score is #@score."    puts "Congratulations, you have won!" if to_enum.any? {|e| e >= @win_limit}  end   def no_more_moves?; @no_more_moves; end  def won?;  to_enum.any? {|e| e >= @win_limit}; end  def reset!; initialize @size, @win_limit, @cw; end   private   def set x, y, val    @board[y][x] = val  end   def spawn     free_pos = to_enum.select{|elem,x,y| elem == 0}.map{|_,x,y| [x,y]}    unless free_pos.empty?      set *free_pos.sample, rand > 0.1 ? 2 : 4 if @moved    else      snap = @board      unless @stop        @stop = true        %i{up down left right}.each{|s| move(s)}        @no_more_moves = true if snap.flatten == @board.flatten        @board = snap        @stop = false      end    end  end   def logic list    jump = false    result =    list.reduce([]) do |res, val|      if res.last == val && !jump	res[-1] += val	@score += val        jump = true      elsif val != 0	res.push val        jump = false      end      res    end    result += [0] * (@size - result.length)    @moved ||= list != result    result  end   def column_map    xboard = @board.transpose    xboard.map!{|c| yield c }    xboard.transpose  end   def row_map    @board.map {|r| yield r }  end   def to_enum    @enum ||= Enumerator.new(@size * @size) do |yielder|      (@size*@size).times do |i|	yielder.yield (@board[i / @size][i % @size]), (i % @size), (i / @size )      end    end    @enum.rewind  end   def format(num)    if \$color      cstart = "\e[" + \$colors[Math.log(num, 2)] + "m"      cend = "\e[0m"    else      cstart = cend = ""    end    cstart + num.to_s.center(@cw) + cend  endend \$color = true\$colors = %W{0 1;97 1;93 1;92 1;96 1;91 1;95 1;94 1;30;47 1;43 1;421;46 1;41 1;45 1;44 1;33;43 1;33;42 1;33;41 1;33;44}\$rumble = false \$check_score = trueunless ARGV.empty?  puts "Usage: #\$0 [gridsize] [score-threshold] [padwidth] [--no-color] [--rumble]"; exit if %W[-h --help].include?(ARGV[0])  args = ARGV.map(&:to_i).reject{|n| n == 0}  b = Board.new(*args) unless args.empty?  \$rumble = true if ARGV.any?{|a| a =~ /rumble/i }  \$color = false if ARGV.any?{|a| a =~ /no.?color/i}end b ||= Board.newputs "\e[H\e[2J"b.drawputs "Press h for help, q to quit"loop do  input = STDIN.getch  if input == "\e"     2.times {input << STDIN.getch}  end   case input  when "\e[A", "w" then b.move(:up)  when "\e[B", "s" then b.move(:down)  when "\e[C", "d" then b.move(:right)  when "\e[D", "a" then b.move(:left)   when "q","\u0003","\u0004"  then b.print_score; exit   when "h"     puts <<-EOM.gsub(/^\s*/, '')      ┌─                                                                                  ─┐      │Use the arrow-keys or WASD on your keyboard to push board in the given direction.         │Tiles with the same number merge into one.                                                │Get a tile with a value of #{ARGV[1] || 2048} to win.                                     │In case you cannot move or merge any tiles anymore, you loose.                            │You can start this game with different settings by providing commandline argument:        │For instance:                                                                             │  %> #\$0 6 8192 --rumble                                                                  └─                                                                                  ─┘      PRESS q TO QUIT (or Ctrl-C or Ctrl-D)    EOM    input = STDIN.getch  end   puts "\e[H\e[2J"  b.draw   if b.no_more_moves? or \$check_score && b.won?    b.print_score    if b.no_more_moves?      puts "No more moves possible"      puts "Again? (y/n)"      exit if STDIN.gets.chomp.downcase == "n"      \$check_score = true      b.reset!      puts "\e[H\e[2J"      b.draw    else      puts "Continue? (y/n)"      exit if STDIN.gets.chomp.downcase == "n"      \$check_score = false      puts "\e[H\e[2J"      b.draw    end  endend `

## Rust

### Text mode

A simple implementation in rust. The user has to input an endline since i did not find a way to read a key press

Library: rand
` use std::io::{self,BufRead};extern crate rand; enum Usermove {    Up,    Down,    Left,    Right,} fn print_game(field :& [[u32;4];4] ){    println!("{:?}",&field[0] );    println!("{:?}",&field[1] );    println!("{:?}",&field[2] );    println!("{:?}",&field[3] );} fn get_usermove()-> Usermove {    let umove: Usermove ;    loop{        let mut input = String::new();        io::stdin().read_line(&mut input).unwrap();         match input.chars().nth(0){            Some('a') =>{umove = Usermove::Left ;break },            Some('w') =>{umove = Usermove::Up   ;break },            Some('s') =>{umove = Usermove::Down ;break },            Some('d') =>{umove = Usermove::Right;break },            _   => {println!("input was {}: invalid character should be a,s,w or d ",input.chars().nth(0).unwrap());} ,        }    }    umove} //this function inplements the user moves.//for every element it looks if the element is zero// if the element is zero it looks against the direction of the movement if any//element is not zero then it will move it to the element its place then it will look for//a matching element//  if the element is not zero then it will look for a match if no match is found// then it will look for the next element fn do_game_step(step : &Usermove, field:&mut [[u32;4];4]){    match *step {        Usermove::Left =>{            for array in field{                for  col in 0..4 {                    for testcol in (col+1)..4 {                        if array[testcol] != 0 {                            if array[col] == 0 {                                array[col] += array[testcol];                                array[testcol] = 0;                            }                            else if array[col] == array[testcol] {                                array[col] += array[testcol];                                array[testcol] = 0;                                break;                            } else {                                break                            }                        }                    }                }            }        } ,        Usermove::Right=>{            for array in field{                for  col in (0..4).rev() {                    for testcol in (0..col).rev() {                        if array[testcol] != 0 {                            if array[col] == 0 {                                array[col] += array[testcol];                                array[testcol] = 0;                            }                            else if array[col] == array[testcol] {                                array[col] += array[testcol];                                array[testcol] = 0;                                break;                            }else {                                break;                            }                        }                    }                }            }        } ,        Usermove::Down   =>{            for col in 0..4 {                for row in (0..4).rev() {                    for testrow in (0..row).rev() {                        if field[testrow][col] != 0 {                            if field[row][col] == 0 {                                field[row][col] += field[testrow][col];                                field[testrow][col] = 0;                            } else if field[row][col] == field[testrow][col] {                                field[row][col] += field[testrow][col];                                field[testrow][col] = 0;                                break;                            }else {                                break;                            }                         }                    }                }            }        } ,        Usermove::Up =>{            for col in 0..4 {                for row in 0..4{                    for testrow in (row+1)..4 {                        if field[testrow][col] != 0 {                            if field[row][col] == 0 {                                field[row][col] += field[testrow][col];                                field[testrow][col] = 0;                            } else if field[row][col] == field[testrow][col] {                                field[row][col] += field[testrow][col];                                field[testrow][col] = 0;                                break;                            }else {                                break;                            }                        }                    }                }            }        },    }} fn spawn( field: &mut  [[u32;4];4]){    loop{        let x = rand::random::<usize>();        if field[x % 4][(x/4)%4] == 0 {            if x % 10 == 0 {                field[x % 4][(x/4)%4]= 4;            }else{                field[x % 4][(x/4)%4]= 2;            }            break;        }    }}  fn main() {    let mut field : [[u32; 4];4] =  [[0;4];4];    let mut test : [[u32; 4];4] ;    'gameloop:loop {        //check if there is still an open space        test=field.clone();        spawn(&mut field);        //if all possible moves do not yield a change then there is no valid move left        //and it will be game over        for i in [Usermove::Up,Usermove::Down,Usermove::Left,Usermove::Right].into_iter(){            do_game_step(i, &mut test);            if test != field{                break;//found a valid move            }            match *i{                Usermove::Right=> {                    println!("No more valid move, you lose");                    break 'gameloop;                },                _=>{},            }        }        print_game(&field);        println!("move the blocks");         test=field.clone();        while test==field {            do_game_step(&get_usermove(), &mut field);        }         for row in field.iter(){            if row.iter().any(|x| *x == 2048){                print_game(&field );                println!("You Won!!");                break;            }        }    }} `

## Scala

`import java.awt.event.{KeyAdapter, KeyEvent, MouseAdapter, MouseEvent}import java.awt.{BorderLayout, Color, Dimension, Font, Graphics2D, Graphics, RenderingHints}import java.util.Random import javax.swing.{JFrame, JPanel, SwingUtilities} object Game2048 {  val target = 2048  var highest = 0   def main(args: Array[String]): Unit = {    SwingUtilities.invokeLater(() => {      val f = new JFrame      f.setDefaultCloseOperation(3)      f.setTitle("2048")      f.add(new Game, BorderLayout.CENTER)      f.pack()      f.setLocationRelativeTo(null)      f.setVisible(true)    })  }   class Game extends JPanel {    private val (rand , side)= (new Random, 4)    private var (tiles, gamestate)= (Array.ofDim[Tile](side, side), Game2048.State.start)     final private val colorTable =      Seq(new Color(0x701710), new Color(0xFFE4C3), new Color(0xfff4d3), new Color(0xffdac3), new Color(0xe7b08e), new Color(0xe7bf8e),        new Color(0xffc4c3), new Color(0xE7948e), new Color(0xbe7e56), `