I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# 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.

Rules of the game
•   The rules are that on each turn the player must choose a direction   (up, down, left or right).
•   All tiles move as far as possible in that direction, some move 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).
•   Adding a new tile on a blank space.   Most of the time,   a new   2   is to be added,   and occasionally   (10% of the time),   a   4.
•   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 shall indicate which combination 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]
```

•   Check for valid moves.   The player shouldn't be able to skip their turn by trying a move that doesn't change the board.
•   Check for a  win condition.
•   Check for a lose condition.

## AArch64 Assembly

Works with: as version Raspberry Pi 3B version Buster 64 bits
` /* ARM assembly AARCH64 Raspberry PI 3B *//*  program 2048_64.s   */  /*******************************************//* Constantes file                         *//*******************************************//* for this file see task include a file in language AArch64 assembly*/.include "../includeConstantesARM64.inc".equ SIZE,       4       .equ TOTAL,      2048.equ BUFFERSIZE, 80 .equ KEYSIZE,    8.equ IOCTL,     0x1D  // Linux syscall.equ SIGACTION, 0x86  // Linux syscall.equ SYSPOLL,   0x16  // Linux syscall.equ CREATPOLL, 0x14  // Linux syscall.equ CTLPOLL,   0x15  // Linux syscall .equ TCGETS,    0x5401.equ TCSETS,    0x5402.equ ICANON,    2.equ ECHO,     10.equ POLLIN,    1.equ EPOLL_CTL_ADD,    1 .equ SIGINT,   2      // Issued if the user sends an interrupt signal (Ctrl + C).equ SIGQUIT,  3      // Issued if the user sends a quit signal (Ctrl + D).equ SIGTERM, 15      // Software termination signal (sent by kill by default).equ SIGTTOU, 22 /*******************************************//* Structures                               *//********************************************//* structure termios see doc linux*/    .struct  0term_c_iflag:                    // input modes    .struct  term_c_iflag + 4 term_c_oflag:                    // output modes    .struct  term_c_oflag + 4 term_c_cflag:                    // control modes    .struct  term_c_cflag + 4 term_c_lflag:                    // local modes    .struct  term_c_lflag + 4 term_c_cc:                       // special characters    .struct  term_c_cc + 40      // see length if necessary term_fin: /* structure sigaction see doc linux */    .struct  0sa_handler:    .struct  sa_handler + 8 sa_mask:    .struct  sa_mask + 8 sa_flags:    .struct  sa_flags + 8sa_sigaction:    .struct  sa_sigaction + 8sa_fin: /* structure poll see doc linux */    .struct  0poll_event:                        //  events mask    .struct  poll_event + 8poll_fd:                           // events returned    .struct  poll_fd  + 8poll_fin:/*********************************//* Initialized data              *//*********************************/.dataszMessOK:           .asciz "Bravo !! You win. \n"szMessNotOK:        .asciz "You lost !! \n"szMessNewGame:      .asciz "New game (y/n) ? \n"szMessErreur:       .asciz "Error detected.\n"szMessErrInitTerm:  .asciz "Error terminal init.\n"szMessErrInitPoll:  .asciz "Error poll init.\n"szMessErreurKey:    .asciz "Error read key.\n"szMessErr:          .asciz    "Error code hexa : @ décimal : @ \n"szCarriageReturn:   .asciz "\n"szMess0:            .asciz "      "szMess2:            .asciz "   2  "szMess4:            .asciz "   4  "szMess8:            .asciz "   8  "szMess16:           .asciz "  16  "szMess32:           .asciz "  32  "szMess64:           .asciz "  64  "szMess128:          .asciz " 128  "szMess256:          .asciz " 256  "szMess512:          .asciz " 512  "szMess1024:         .asciz " 1024 "szMess2048:         .asciz " 2048 "szCleax1:           .byte 0x1B                     .byte 'c'           // other console clear                    .byte 0 szLineH:            .asciz "-----------------------------\n"szLineV:            .asciz "|"szLineVT:           .asciz "|      |      |      |      |\n".align 4qGraine:            .quad 123456/*********************************//* UnInitialized data            *//*********************************/.bss.align 4sZoneConv:      .skip 24sBuffer:        .skip BUFFERSIZEqTbCase:        .skip 8 * SIZE * SIZEqEnd:           .skip 8                        // 0 loop  1 = end loopqTouche:        .skip KEYSIZE                  // value key pressedstOldtio:       .skip term_fin                 // old terminal statestCurtio:       .skip term_fin                 // current terminal statestSigAction:    .skip sa_fin                   // area signal structurestSigAction1:   .skip sa_finstSigAction2:   .skip sa_finstSigAction3:   .skip sa_finstPoll1:        .skip poll_fin                 // area poll structurestPoll2:        .skip poll_finstevents:       .skip 16/*********************************//*  code section                 *//*********************************/.text.global mainmain:                                // entry of program     mov x0,#0    bl initTerm                      // terminal init    cmp x0,0                         // error ?    blt 100f    bl initPoll                      // epoll instance init    cmp x0,0    blt 99f    mov x22,x0                        // save epfd1:                                    // begin game loop    ldr x0,qAdrszCleax1    bl affichageMess    bl razTable2:    bl addDigit       cmp x0,#-1    beq 5f                            // end game    bl displayGame3:    mov x0,x22    bl waitKey    cmp x0,0    beq 3b    bl readKey    cmp x0,#-1    beq 99f                          // error    bl keyMove    cmp x0,#0    beq 3b                            // no change -> loop    cmp x0,#2                         // last addition = 2048 ?    beq 4f    cmp x0,#-1                        // quit ?    bne 2b                            // loop     b 10f4:                                    // last addition = 2048     ldr x0,qAdrszMessOK    bl affichageMess    b 10f5:                                    // display message no solution    ldr x0,qAdrszMessNotOK    bl affichageMess 10:                                   // display new game ?    ldr x0,qAdrszCarriageReturn    bl affichageMess    ldr x0,qAdrszMessNewGame    bl affichageMess11:    mov x0,x22    bl waitKey    cmp x0,0    beq 11b    bl readKey    ldr x0,qAdrqTouche    ldrb w0,[x0]    cmp w0,#'y'    beq 1b    cmp w0,#'Y'    beq 1b99:    bl restauTerm                     // terminal restaur100:                                  // standard end of the program     mov x0, #0                        // return code    mov x8, #EXIT                     // request to exit program    svc #0                            // perform the system call qAdrszCarriageReturn:     .quad szCarriageReturnqAdrszMessNotOK:          .quad szMessNotOKqAdrszMessOK:             .quad szMessOKqAdrszMessNewGame:        .quad szMessNewGameqAdrsZoneConv:            .quad sZoneConvqAdrszCleax1:             .quad szCleax1qAdrszMessErrInitTerm:    .quad szMessErrInitTermqAdrszMessErrInitPoll:    .quad szMessErrInitPollqAdrszMessErreurKey:      .quad szMessErreurKeyqAdrstOldtio:             .quad stOldtioqAdrstCurtio:             .quad stCurtioqAdrstSigAction:          .quad stSigActionqAdrstSigAction1:         .quad stSigAction1qAdrSIG_IGN:              .quad 1qAdrqEnd:                 .quad qEndqAdrqTouche:              .quad qToucheqAdrstevents:             .quad stevents/******************************************************************//*     raz table cases                                                   */ /******************************************************************/razTable:    stp x0,lr,[sp,-16]!     // save  registres    stp x1,x2,[sp,-16]!     // save  registres    ldr x1,qAdrqTbCase    mov x2,#01:    str xzr,[x1,x2,lsl #3]    add x2,x2,#1    cmp x2,#SIZE * SIZE    blt 1b100:    ldp x1,x2,[sp],16       // restaur des  2 registres    ldp x0,lr,[sp],16       // restaur des  2 registres    ret/******************************************************************//*     key move                                                   */ /******************************************************************//* x0 contains key value               */keyMove:    stp x1,lr,[sp,-16]!     // save  registres    lsr x0,x0,#16    cmp x0,#0x42                  // down arrow     bne 1f    bl moveDown    b 100f1:    cmp x0,#0x41                  // high arrow    bne 2f    bl moveUp    b 100f2:    cmp x0,#0x43                  // right arrow    bne 3f    bl moveRight    b 100f3:    cmp x0,#0x44                  // left arrow    bne 4f    bl moveLeft    b 100f4:    ldr x0,qAdrqTouche    ldrb w0,[x0]    cmp w0,#'q'                   // quit game    bne 5f    mov x0,#-1    b 100f5:    cmp w0,#'Q'                   // quit game    bne 100f    mov x0,#-1    b 100f 100:    ldp x1,lr,[sp],16       // restaur des  2 registres    ret/******************************************************************//*           move left                                   */ /******************************************************************//* x0 return -1 if ok     */moveLeft:    stp x1,lr,[sp,-16]!          // save  registres    stp x2,x3,[sp,-16]!          // save  registres    stp x4,x5,[sp,-16]!          // save  registres    stp x6,x7,[sp,-16]!          // save  registres    stp x8,x9,[sp,-16]!          // save  registres    stp x10,x11,[sp,-16]!        // save  registres    ldr x1,qAdrqTbCase    mov x0,#0                   // top move Ok    mov x2,#0                   // line indice1:    mov x6,#0                   // counter empty case    mov x7,#0                   // first digit    mov x10,#0                  // last digit to add    mov x3,#0                   // column indice2:    lsl x5,x2,#2                // change this if size <> 4    add x5,x5,x3                // compute table indice    ldr x4,[x1,x5,lsl #3]    cmp x4,#0    cinc x6,x6,eq               // positions vides    beq 5f    cmp x6,#0    beq 3f                      // no empty left case    mov x8,#0    str x8,[x1,x5,lsl #3]       // raz digit    sub x5,x5,x6    str x4,[x1,x5,lsl #3]       // and store to left empty position    mov x0,#1                   // move Ok3:    cmp x7,#0                   // first digit    beq 4f    cmp x10,x4                  // prec digit have to add     beq 4f    sub x8,x5,#1                // prec digit     ldr x9,[x1,x8,lsl #3]    cmp x4,x9                   // equal ?    bne 4f    mov x10,x4                  // save digit     add x4,x4,x9                // yes -> add    str x4,[x1,x8,lsl #3]    cmp x4,#TOTAL    beq 6f    mov x4,#0    str x4,[x1,x5,lsl #3]    add x6,x6,#1                // empty case + 1    mov x0,#1                   // move Ok4:    add x7,x7,#1                // no first digit 5:                              // and loop    add x3,x3,#1    cmp x3,#SIZE    blt 2b    add x2,x2,#1    cmp x2,#SIZE    blt 1b    b 100f6:    mov x0,#2                   // total = 2048100:    ldp x10,x11,[sp],16         // restaur des  2 registres    ldp x8,x9,[sp],16           // restaur des  2 registres    ldp x6,x7,[sp],16           // restaur des  2 registres    ldp x4,x5,[sp],16           // restaur des  2 registres    ldp x2,x3,[sp],16           // restaur des  2 registres    ldp x1,lr,[sp],16           // restaur des  2 registres    ret/******************************************************************//*           move right                                   */ /******************************************************************//* x0 return -1 if ok     */moveRight:    stp x1,lr,[sp,-16]!          // save  registres    stp x2,x3,[sp,-16]!          // save  registres    stp x4,x5,[sp,-16]!          // save  registres    stp x6,x7,[sp,-16]!          // save  registres    stp x8,x9,[sp,-16]!          // save  registres    stp x10,x11,[sp,-16]!        // save  registres    ldr x1,qAdrqTbCase    mov x0,#0    mov x2,#01:    mov x6,#0    mov x7,#0    mov x10,#0    mov x3,#SIZE-12:    lsl x5,x2,#2                  // change this if size <> 4    add x5,x5,x3    ldr x4,[x1,x5,lsl #3]    cmp x4,#0    cinc x6,x6,eq               // positions vides    beq 5f     cmp x6,#0    beq 3f                      // no empty right case    mov x0,#0    str x0,[x1,x5,lsl #3]       // raz digit    add x5,x5,x6    str x4,[x1,x5,lsl #3]       // and store to right empty position    mov x0,#13:    cmp x7,#0                   // first digit    beq 4f    add x8,x5,#1                // next digit     ldr x9,[x1,x8,lsl #3]    cmp x4,x9                   // equal ?    bne 4f    cmp x10,x4    beq 4f    mov x10,x4    add x4,x4,x9                // yes -> add    str x4,[x1,x8,lsl #3]    cmp x4,#TOTAL    beq 6f    mov x4,#0    str x4,[x1,x5,lsl #3]    add x6,x6,#1                // empty case + 1    mov x0,#14:    add x7,x7,#1                // no first digit 5:                              // and loop    sub x3,x3,#1    cmp x3,#0    bge 2b    add x2,x2,#1    cmp x2,#SIZE    blt 1b    b 100f6:    mov x0,#2100:    ldp x10,x11,[sp],16         // restaur des  2 registres    ldp x8,x9,[sp],16           // restaur des  2 registres    ldp x6,x7,[sp],16           // restaur des  2 registres    ldp x4,x5,[sp],16           // restaur des  2 registres    ldp x2,x3,[sp],16           // restaur des  2 registres    ldp x1,lr,[sp],16           // restaur des  2 registres    ret/******************************************************************//*           move down                                   */ /******************************************************************//* x0 return -1 if ok     */moveDown:    stp x1,lr,[sp,-16]!          // save  registres    stp x2,x3,[sp,-16]!          // save  registres    stp x4,x5,[sp,-16]!          // save  registres    stp x6,x7,[sp,-16]!          // save  registres    stp x8,x9,[sp,-16]!          // save  registres    stp x10,x11,[sp,-16]!        // save  registres    ldr x1,qAdrqTbCase    mov x0,#0    mov x3,#01:    mov x6,#0    mov x7,#0    mov x10,#0    mov x2,#SIZE-12:    lsl x5,x2,#2                  // change this if size <> 4    add x5,x5,x3    ldr x4,[x1,x5,lsl #3]    cmp x4,#0    cinc x6,x6,eq               // positions vides    beq 5f    cmp x6,#0    beq 3f                      // no empty right case    mov x0,#0    str x0,[x1,x5,lsl #3]       // raz digit    lsl x0,x6,#2    add x5,x5,x0    str x4,[x1,x5,lsl #3]       // and store to right empty position    mov x0,#13:    cmp x7,#0                   // first digit    beq 4f    add x8,x5,#SIZE                // down digit     ldr x9,[x1,x8,lsl #3]    cmp x4,x9                   // equal ?    bne 4f    cmp x10,x4    beq 4f    mov x10,x4    add x4,x4,x9                // yes -> add    str x4,[x1,x8,lsl #3]    cmp x4,#TOTAL    beq 6f    mov x4,#0    str x4,[x1,x5,lsl #3]    add x6,x6,#1                // empty case + 1    mov x0,#14:    add x7,x7,#1                   // no first digit 5:                           // and loop    sub x2,x2,#1    cmp x2,#0    bge 2b    add x3,x3,#1    cmp x3,#SIZE    blt 1b    b 100f6:    mov x0,#2100:    ldp x10,x11,[sp],16         // restaur des  2 registres    ldp x8,x9,[sp],16           // restaur des  2 registres    ldp x6,x7,[sp],16           // restaur des  2 registres    ldp x4,x5,[sp],16           // restaur des  2 registres    ldp x2,x3,[sp],16           // restaur des  2 registres    ldp x1,lr,[sp],16           // restaur des  2 registres    ret/******************************************************************//*           move up                                   */ /******************************************************************//* x0 return -1 if ok     */moveUp:    stp x1,lr,[sp,-16]!          // save  registres    stp x2,x3,[sp,-16]!          // save  registres    stp x4,x5,[sp,-16]!          // save  registres    stp x6,x7,[sp,-16]!          // save  registres    stp x8,x9,[sp,-16]!          // save  registres    stp x10,x11,[sp,-16]!        // save  registres    ldr x1,qAdrqTbCase    mov x0,#0    mov x3,#01:    mov x6,#0    mov x7,#0    mov x10,#0    mov x2,#02:    lsl x5,x2,#2                  // change this if size <> 4    add x5,x5,x3    ldr x4,[x1,x5,lsl #3]    cmp x4,#0    cinc x6,x6,eq               // positions vides    beq 5f    cmp x6,#0    beq 3f                      // no empty right case    mov x0,#0    str x0,[x1,x5,lsl #3]       // raz digit    lsl x0,x6,#2    sub x5,x5,x0    str x4,[x1,x5,lsl #3]       // and store to right empty position    mov x0,#13:    cmp x7,#0                   // first digit    beq 4f    sub x8,x5,#SIZE             // up digit     ldr x9,[x1,x8,lsl #3]    cmp x4,x9                   // equal ?    bne 4f    cmp x10,x4    beq 4f    mov x10,x4    add x4,x4,x9                // yes -> add    str x4,[x1,x8,lsl #3]    cmp x4,#TOTAL    beq 6f    mov x4,#0    str x4,[x1,x5,lsl #3]    add x6,x6,#1                // empty case + 1    mov x0,#14:    add x7,x7,#1                // no first digit 5:                              // and loop    add x2,x2,#1    cmp x2,#SIZE    blt 2b    add x3,x3,#1    cmp x3,#SIZE    blt 1b    b 100f6:    mov x0,#2100:    ldp x10,x11,[sp],16         // restaur des  2 registres    ldp x8,x9,[sp],16           // restaur des  2 registres    ldp x6,x7,[sp],16           // restaur des  2 registres    ldp x4,x5,[sp],16           // restaur des  2 registres    ldp x2,x3,[sp],16           // restaur des  2 registres    ldp x1,lr,[sp],16           // restaur des  2 registres    ret/******************************************************************//*           add new digit on game                                   */ /******************************************************************//* x0 return -1 if ok     */addDigit:    stp x1,lr,[sp,-16]!          // save  registres    stp x2,x3,[sp,-16]!          // save  registres    stp x4,x5,[sp,-16]!          // save  registres    sub sp,sp,#8 * SIZE*SIZE    mov fp,sp     mov x0,#100    bl genereraleas    cmp x0,#10    mov x1,#4    mov x5,#2    csel x5,x5,x1,ge   // movlt x5,#4    //movge x5,#2    ldr x1,qAdrqTbCase    mov x3,#0    mov x4,#01:      ldr x2,[x1,x3,lsl 3]    cmp x2,#0    bne 2f    str x3,[fp,x4,lsl 3]    add x4,x4,#12:    add x3,x3,#1    cmp x3,#SIZE*SIZE    blt 1b    cmp x4,#0                 // no empty case    beq 4f    cmp x4,#1    bne 3f    ldr x2,[fp]               // one case    str x5,[x1,x2,lsl 3]    mov x0,#0    b 100f3:                            // multiple case    sub x0,x4,#1    bl genereraleas    ldr x2,[fp,x0,lsl 3]    str x5,[x1,x2,lsl 3]    mov x0,#0    b 100f4:    mov x0,#-1100:    add sp,sp,#8*  (SIZE*SIZE)  // stack alignement    ldp x4,x5,[sp],16           // restaur des  2 registres    ldp x2,x3,[sp],16           // restaur des  2 registres    ldp x1,lr,[sp],16           // restaur des  2 registres    retqAdrqTbCase:         .quad qTbCase/******************************************************************//*            display game                                      */ /******************************************************************/displayGame:    stp x1,lr,[sp,-16]!          // save  registres    stp x2,x3,[sp,-16]!          // save  registres    ldr x0,qAdrszCleax1    bl affichageMess    ldr x0,qAdrszLineH    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess    ldr x1,qAdrqTbCase    mov x2,#01:    ldr x0,[x1,x2,lsl #3]    bl digitString    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess    add x2,x2,#1    cmp x2,#SIZE    blt 1b    ldr x0,qAdrszCarriageReturn    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineH    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess2:    ldr x0,[x1,x2,lsl #3]    bl digitString    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess    add x2,x2,#1    cmp x2,#SIZE*2    blt 2b    ldr x0,qAdrszCarriageReturn    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineH    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess3:    ldr x0,[x1,x2,lsl #3]    bl digitString    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess    add x2,x2,#1    cmp x2,#SIZE*3    blt 3b    ldr x0,qAdrszCarriageReturn    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineH    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess4:    ldr x0,[x1,x2,lsl #3]    bl digitString    bl affichageMess    ldr x0,qAdrszLineV    bl affichageMess    add x2,x2,#1    cmp x2,#SIZE*4    blt 4b    ldr x0,qAdrszCarriageReturn    bl affichageMess    ldr x0,qAdrszLineVT    bl affichageMess    ldr x0,qAdrszLineH    bl affichageMess 100:    ldp x2,x3,[sp],16           // restaur des  2 registres    ldp x1,lr,[sp],16           // restaur des  2 registres    retqAdrszLineH:         .quad szLineHqAdrszLineV:         .quad szLineVqAdrszLineVT:        .quad szLineVT/******************************************************************//*            digits string                                       */ /******************************************************************//* x0 contains number *//* x0 return address string */digitString:    stp x1,lr,[sp,-16]!          // save  registres    cmp x0,#0    bne 1f    ldr x0,qAdrszMess0    b 100f1:    cmp x0,#2    bne 2f    ldr x0,qAdrszMess2    b 100f2:    cmp x0,#4    bne 3f    ldr x0,qAdrszMess4    b 100f3:    cmp x0,#8    bne 4f    ldr x0,qAdrszMess8    b 100f4:    cmp x0,#16    bne 5f    ldr x0,qAdrszMess16    b 100f5:    cmp x0,#32    bne 6f    ldr x0,qAdrszMess32    b 100f6:    cmp x0,#64    bne 7f    ldr x0,qAdrszMess64    b 100f7:    cmp x0,#128    bne 8f    ldr x0,qAdrszMess128    b 100f8:    cmp x0,#256    bne 9f    ldr x0,qAdrszMess256    b 100f9:    cmp x0,#512    bne 10f    ldr x0,qAdrszMess512    b 100f10:    cmp x0,#1024    bne 11f    ldr x0,qAdrszMess1024    b 100f11:    cmp x0,#2048    bne 12f    ldr x0,qAdrszMess2048    b 100f12:    ldr x1,qAdrszMessErreur                       // error message    bl   displayError100:    ldp x1,lr,[sp],16           // restaur des  2 registres    retqAdrszMess0:          .quad szMess0qAdrszMess2:          .quad szMess2qAdrszMess4:          .quad szMess4qAdrszMess8:          .quad szMess8qAdrszMess16:         .quad szMess16qAdrszMess32:         .quad szMess32qAdrszMess64:         .quad szMess64qAdrszMess128:        .quad szMess128qAdrszMess256:        .quad szMess256qAdrszMess512:        .quad szMess512qAdrszMess1024:       .quad szMess1024qAdrszMess2048:        .quad szMess2048 //qAdrsBuffer:         .quad sBufferqAdrszMessErreur :        .quad szMessErreur  /***************************************************//*   Generation random number                  *//***************************************************//* x0 contains limit  */genereraleas:    stp x1,lr,[sp,-16]!            // save  registers    stp x2,x3,[sp,-16]!            // save  registers    ldr x1,qAdrqGraine    ldr x2,[x1]    ldr x3,qNbDep1    mul x2,x3,x2    ldr x3,qNbDep2    add x2,x2,x3    str x2,[x1]                    // maj de la graine pour l appel suivant     cmp x0,#0    beq 100f    udiv x3,x2,x0    msub x0,x3,x0,x2               // résult = remainder 100:                               // end function    ldp x2,x3,[sp],16              // restaur  2 registers    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*****************************************************/qAdrqGraine: .quad qGraineqNbDep1:     .quad 0x0019660dqNbDep2:     .quad 0x3c6ef35f /******************************************************************//*     traitement du signal                                       */ /******************************************************************/sighandler:    stp x0,lr,[sp,-16]!            // save  registers    str x1,[sp,-16]!     ldr x0,qAdrqEnd    mov x1,#1                      // maj zone end    str x1,[x0]    ldr x1,[sp],16    ldp x0,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/***************************************************//*   display error message                         *//***************************************************//* x0 contains error code  x1 : message address */displayError:    stp x2,lr,[sp,-16]!            // save  registers    mov x2,x0                      // save error code    mov x0,x1    bl affichageMess    mov x0,x2                      // error code    ldr x1,qAdrsZoneConv    bl conversion16                // conversion hexa    ldr x0,qAdrszMessErr           // display error message    ldr x1,qAdrsZoneConv    bl strInsertAtCharInc               // insert result at @ character    mov x3,x0    mov x0,x2                      // error code    ldr x1,qAdrsZoneConv               // result address    bl conversion10S                // conversion decimale    mov x0,x3    ldr x1,qAdrsZoneConv    bl strInsertAtCharInc               // insert result at @ character    bl affichageMess100:    ldp x2,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30qAdrszMessErr:                 .quad szMessErr/*********************************//* init terminal state            *//*********************************/initTerm:    stp x1,lr,[sp,-16]!            // save  registers    /* read terminal state */    mov x0,STDIN                   // input console    mov x1,TCGETS    ldr x2,qAdrstOldtio    mov x8,IOCTL                   // call system Linux    svc 0     cbnz x0,98f                    // error ?     adr x0,sighandler              // adresse routine traitement signal    ldr x1,qAdrstSigAction         // adresse structure sigaction    str x0,[x1,sa_handler]         // maj handler    mov x0,SIGINT                  // signal type    ldr x1,qAdrstSigAction    mov x2,0    mov x3,8    mov x8,SIGACTION               // call system    svc 0      cmp x0,0                       // error ?    bne 98f    mov x0,SIGQUIT    ldr x1,qAdrstSigAction    mov x2,0                       // NULL    mov x8,SIGACTION               // call system     svc 0     cmp x0,0                       // error ?    bne 98f    mov x0,SIGTERM    ldr x1,qAdrstSigAction    mov x2,0                       // NULL    mov x8,SIGACTION               // appel systeme     svc 0     cmp x0,0    bne 98f    //    adr x0,qAdrSIG_IGN             // address signal igonre function    ldr x1,qAdrstSigAction1    str x0,[x1,sa_handler]    mov x0,SIGTTOU                 //invalidate other process signal    ldr x1,qAdrstSigAction1    mov x2,0                       // NULL    mov x8,SIGACTION               // call system     svc 0     cmp x0,0    bne 98f    //    /* read terminal current state  */    mov x0,STDIN    mov x1,TCGETS    ldr x2,qAdrstCurtio            // address current termio    mov x8,IOCTL                   // call systeme     svc 0     cmp x0,0                       // error ?    bne 98f    mov x2,ICANON | ECHO           // no key pressed echo on display    mvn x2,x2                      // and one key     ldr x1,qAdrstCurtio    ldr x3,[x1,#term_c_lflag]    and x3,x2,x2                   // add flags     str x3,[x1,#term_c_lflag]      // and store    mov x0,STDIN                   // maj terminal current state     mov x1,TCSETS    ldr x2,qAdrstCurtio    mov x8,IOCTL                   // call system    svc 0     cbz x0,100f98:                                // error display    ldr x1,qAdrszMessErrInitTerm    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30qAdrstSigAction2:    .quad stSigAction2qAdrstSigAction3:    .quad stSigAction3/*********************************//* init instance epool            *//*********************************/initPoll:    stp x1,lr,[sp,-16]!            // save  registers    ldr x0,qAdrstevents    mov x1,STDIN                   // maj structure events    str x1,[x0,#poll_fd]           // maj FD    mov x1,POLLIN                  // action code    str x1,[x0,#poll_event]    mov x0,0    mov x8,CREATPOLL               // create epoll instance    svc 0    cmp x0,0                       // error ?    ble 98f    mov x10,x0                     // return FD epoll instance    mov x1,EPOLL_CTL_ADD    mov x2,STDIN                   // Fd to we want add    ldr x3,qAdrstevents            // structure events address    mov x8,CTLPOLL                 // call system control epoll    svc 0    cmp x0,0                       // error ?    blt 98f                       // no    mov x0,x10                     // return FD epoll instance    b 100f98:                                // error display    ldr x1,qAdrszMessErrInitPoll   // error message    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*********************************//* wait key                      *//*********************************//* x0 contains FD poll    */waitKey:    stp x1,lr,[sp,-16]!            // save  registers    ldr x11,qAdrqTouche            // key address    str xzr,[x11]                  // raz key1:    ldr x1,qAdrqEnd                // if signal ctrl-c  -> end    ldr x1,[x1]    cbnz x1,100f     ldr x1,qAdrstevents    mov x2,12                      // size events    mov x3,1                       // timeout = 1  TODO: ??    mov x4,0    mov x8,SYSPOLL                 // call system wait POLL    svc 0     cmp x0,0                       // key pressed ?    bge 100f98:                                // error display    ldr x1,qAdrszMessErreurKey        // error message    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*********************************//* read key                      *//*********************************//* x0 returns key value */readKey:    stp x1,lr,[sp,-16]!            // save  registers    mov x0,STDIN                   // File Descriptor    ldr x1,qAdrqTouche             // buffer address    mov x2,KEYSIZE                 // key size    mov x8,READ                    // read key    svc #0    cmp x0,0                       // error ?    ble 98f    ldr x2,qAdrqTouche             // key address    ldr x0,[x2]    b 100f98:                                // error display    ldr x1,qAdrszMessErreur        // error message    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*********************************//* restaur terminal state        *//*********************************/restauTerm:    stp x1,lr,[sp,-16]!            // save  registers    mov x0,STDIN                   // end then restaur begin state terminal    mov x1,TCSETS    ldr x2,qAdrstOldtio    mov x8,IOCTL                   // call system      svc 0    cbz x0,100f    ldr x1,qAdrszMessErreur        // error message    bl   displayError100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/********************************************************//*        File Include fonctions                        *//********************************************************//* for this file see task include a file in language AArch64 assembly */.include "../includeARM64.inc" `

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);    Main_Loop: loop      Reset_Game;      Game_Loop: loop             Display_Board;         case Get_Keystroke is            when Restart => exit Game_Loop;            when Quit    => exit Main_Loop;            when Left    => New_Board := Move(Board);            when Right   => New_Board := VFlip(Move(VFlip(Board)));            when Up      => New_Board := Transpose(Move(Transpose(Board)));            when Down    => New_Board := Transpose(VFlip(Move(VFlip(Transpose(Board)))));            when others  => null;         end case;          if New_Board = Board then            Put_Line ("Invalid move...");         elsif (for some Row of New_Board => (for some Cell of Row => Cell = 2048)) then            Display_Board;            Put_Line ("Win !");            exit Main_Loop;         else            Board := New_Board;            Add_Block; -- 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_Loop;            end if;         end if;      end loop Game_Loop;   end loop Main_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))) `

## Amazing Hopper

```VERSION 1: "Hopper" flavour.
```
` #context-free select Position of aleatory tail#context-free show Table#context-free chek Winner or Game Over#context-free print Table structure#proto MovingRightDown(_X_)#proto MovingLeftUp(_X_)#proto checkPointLeftUp(_X_)#proto checkPointRightDown(_X_)#proto checkMoveRightDown(_X_)#proto checkMoveLeftUp(_X_)#define KUP        5#define KDOWN     24#define KLEFT     19#define KRIGHT     4#define KESCAPE   27#define MOVEHORZ   1#define MOVEVERT   0 #define equaltables(_X_,_Y_)   _I_=1,\                               __LOOP_ROW__:,\                                    _J_=1,\                                    __LOOP_COL__:,\                                        [_I_,_J_]get(_X_),get(_Y_),neq? do{{0},jmp(__OUT__LOOP__)},\                                        ++_J_,{4,_J_}jle(__LOOP_COL__),\                                    ++_I_,{4,_I_}jle(__LOOP_ROW__),\                               {1},__OUT__LOOP__:,clear mark#include <hopper.h> main:  .ctrl c  contador de movimientos=0  score=0  table=0,{4,4}zeros array(table)  // create table  oldTable=0, show Structure=1 /* define initial positions */  {""}tok sep  select Position of aleatory tail  select Position of aleatory tail  home  hide cursor  show Table  c=0, go=1,swFin=1  /* game! */  __PLAY_GAME__:      if key pressed?         lastkey(c)         oldTable = table         switch(c)            case(KRIGHT) :: do{ _check Move Right Down(MOVEHORZ), exit }            case(KDOWN)  :: do{ _check Move Right Down(MOVEVERT), exit }            case(KLEFT)  :: do{ _check Move Left Up(MOVEHORZ),    exit }            case(KUP)    :: do{ _check Move Left Up(MOVEVERT),    exit }            case(KESCAPE):: do{ go=0,swFin=0 }         end switch         kbfree         chek Winner or Game Over         {go}do{             if ( not( equal tables(oldTable, table) ) )                select Position  of aleatory tail                ++contador de movimientos             end if             show Table         }     end if     {go},jt(__PLAY_GAME__)      if ( {swFin} )        {"          \LG","YOU WIN!!!\OFF"}     else        {"          \LR","GAME OVER\OFF"}     end if     println     show cursorexit(0) .localsMoving Right Down(tmpTab)   {tmpTab} compact,ht=0,cpy(ht), length,{4}sub,sizet=0,mov(sizet)   clear(tmpTab),{sizet}zerosarray(tmpTab)   {ht,tmpTab}array(CONCAT)   {tmpTab}backMoving Left Up(tmpTab)   {tmpTab} compact,clear(tmpTab),cpy(tmpTab), length,{4}sub,sizet=0,mov(sizet)   {sizet}zero?,not,do{ ht=0,{sizet}zerosarray(ht)   {ht,tmpTab}array(CONCAT) }   {tmpTab}backcheck Point Right Down(tmpTab)  v1=0,v2=0,tScore=0,totScore=0  for(k=4,{k}gthan(1),--k)     [k]get(tmpTab),mov(v1)     [{k}minus(1)]get(tmpTab),mov(v2)     if( {v1} eqto (v2) )         {v1,v2}add,cpy(tScore),[k]put(tmpTab),[{k}minus(1)]{0}put(tmpTab)         {tScore}plus(totScore),mov(totScore)     end if  next  {tmpTab,totScore}backcheck Move Right Down (_DIRECTION_)  tmpTab=0  for(i=1,{i}lethan(4),++i)    if ( {_DIRECTION_} )    // rows or cols??       [i,1:4]   // rows!    else       [1:4,i]   // cols!    end if    get(table), mov(tmpTab)     if( {tmpTab}stats(SUMMATORY) )     // exist numbers in the row??       clear mark       _Moving Right Down(tmpTab),mov(tmpTab)    // move its!       clear mark       _check Point Right Down(tmpTab),plus(score),mov(score)  // check score...       mov(tmpTab)       _Moving Right Down(tmpTab),mov(tmpTab)   // move remanents!       if( {_DIRECTION_} )          [i,1:4]       else          [1:4,i]       end if       {tmpTab}, put(table)    end if  next  clear mark.back check Point Left Up(tmpTab)  v1=0,v2=0,tScore=0,totScore=0  for(k=1,{k}lthan(4),++k)     [k]get(tmpTab),mov(v1)     [{k}plus(1)]get(tmpTab),mov(v2)     if( {v1} eqto (v2) )        {v1,v2}add,cpy(tScore),[k]put(tmpTab),[{k}plus(1)]{0}put(tmpTab)        {tScore}plus(totScore),mov(totScore)     end if  next  {tmpTab,totScore}back check Move Left Up(_DIRECTION_)  tmpTab=0   for(i=1,{i}lethan(4),++i)    if( {_DIRECTION_} )       [i,1:4]    else        [1:4,i]    end if    get(table),mov(tmpTab)    if( {tmpTab}stats(SUMMATORY) )  // exist numbers in the row??       clear mark       _Moving Left Up(tmpTab),mov(tmpTab)  // move its!       clear mark       _check Point Left Up(tmpTab),plus(score),mov(score)  // check score...       mov(tmpTab)       _Moving Left Up(tmpTab),mov(tmpTab)  // move remanents!       if( {_DIRECTION_} )          [i,1:4]       else          [1:4,i]       end if       {tmpTab},put(table)    end if  next  clear mark.back chek Winner or Game Over:  {table}gthan(0),xtonum,stats(SUMMATORY),{16} eq? do{{0}mov(go),{0}mov(swFin),back}  // You loose!  {0}reshape(table),{2048,table}array(SCAN){0} neq? do{{0}mov(go),back}   // you Win!  {4,4}reshape(table)back select Position of aleatory tail:  prec(-1)  __NO_VALID_POS__:     {10}rand, mulby(10),ceil,module(5),x=0,cpy(x),zero?,do{x=1}     {10}rand, mulby(10),ceil,module(5),y=0,cpy(y),zero?,do{y=1}     [x,y]get(table),jnz(__NO_VALID_POS__)  newTail=2  {1}rand,gthan(0.9), do{ newTail=4 }  {newTail},put(table), clear mark.  prec(0)back show Table:  tmpTable=0  {" ",6,table}xtostr,padcenter,mov(tmpTable) // prepare colours of tiles  {"\BGLGR\BK      \OFF","  0   ",tmpTable}   transform, mov(tmpTable)  {"\BGLGR\BK  2   \OFF","  2   ",tmpTable}   transform, mov(tmpTable)  {"\BGLGR\B  4   \OFF","  4   ",tmpTable}    transform, mov(tmpTable)  {"\BGLGR\B  8   \OFF","  8   ",tmpTable}    transform, mov(tmpTable)  {"\BGR\W  16  \OFF","  16  ",tmpTable}      transform, mov(tmpTable)  {"\BGY\BK  32  \OFF","  32  ",tmpTable}     transform, mov(tmpTable)  {"\BGB\W  64  \OFF","  64  ",tmpTable}      transform, mov(tmpTable)  {"\BGLM\BK 128  \OFF"," 128  ",tmpTable}    transform, mov(tmpTable)  {"\BGG\W 256  \OFF"," 256  ",tmpTable}      transform, mov(tmpTable)  {"\BGB\W 512  \OFF"," 512  ",tmpTable}      transform, mov(tmpTable)    {"\BGR\W 1024 \OFF"," 1024 ",tmpTable}      transform, mov(tmpTable)  {"\BGBK\W\ENF 2048 \OFF"," 2048 ",tmpTable} transform, mov(tmpTable)  // and PRINT!!  {show Structure} do{ print Table structure,{0} mov(show Structure) }   clear mark  scrx=2  for (i=1, {i}lethan(4),++i)        {2,scrx}goxy,[1,i]get(tmpTable),print        {4,scrx}goxy,[2,i]get(tmpTable),print        {6,scrx}goxy,[3,i]get(tmpTable),print        {8,scrx}goxy,[4,i]get(tmpTable),print        clear mark        scrx += 7  next   {"\BGB\W\ENF","\n\n\t   2 0 4 8   \OFF\n\n"}  {"Movimiento # ",contador de movimientos,", \INVSCORE=",score,"\OFF\n"}//,tmpTable}  println back print Table structure:  {"┌──────┬──────┬──────┬──────┐\n"},strtoutf8,  {"│ 2048 │ 2048 │ 2048 │ 2048 │\n"},strtoutf8,  {"├──────┼──────┼──────┼──────┤\n"},strtoutf8,  {"│ 2048 │ 2048 │ 2048 │ 2048 │\n"},strtoutf8,  {"├──────┼──────┼──────┼──────┤\n"},strtoutf8,  {"│ 2048 │ 2048 │ 2048 │ 2048 │\n"},strtoutf8,  {"├──────┼──────┼──────┼──────┤\n"},strtoutf8,  {"│ 2048 │ 2048 │ 2048 │ 2048 │\n"},strtoutf8,  {"└──────┴──────┴──────┴──────┘"},strtoutf8,  printlnback `
Output:
```Begin play...
┌──────┬──────┬──────┬──────┐
│      │      │  2   │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│      │      │      │      │
├──────┼──────┼──────┼──────┤
│  2   │      │      │      │
└──────┴──────┴──────┴──────┘
2 0 4 8

Movimiento # 0, SCORE=0

```
```Playing...
┌──────┬──────┬──────┬──────┐
│      │  2   │      │      │
├──────┼──────┼──────┼──────┤
│      │      │  2   │  4   │
├──────┼──────┼──────┼──────┤
│      │  2   │  32  │ 128  │
├──────┼──────┼──────┼──────┤
│      │  8   │  4   │  16  │
└──────┴──────┴──────┴──────┘
2 0 4 8

Movimiento # 90, SCORE=940
```
```Game Over...
┌──────┬──────┬──────┬──────┐
│  2   │  16  │  2   │  4   │
├──────┼──────┼──────┼──────┤
│  4   │ 128  │  4   │  8   │
├──────┼──────┼──────┼──────┤
│  16  │  8   │ 512  │  4   │
├──────┼──────┼──────┼──────┤
│  2   │  4   │  32  │  2   │
└──────┴──────┴──────┴──────┘
2 0 4 8

Movimiento # 347, SCORE=5040

GAME OVER
\$
```
```VERSION 2: "Hopper-BASIC" flavour.
```
`  // Definicion de "contextos"#context-free select Position of aleatory tail   set Decimal (-1)   tSIZE=0,    Let( tSIZE := var(SIZE) Plus (1) )__NO_VALID_POS__:      x=0, y=0      When( Is Zero? ( Ceil( Rand(10) Mul by(10) ) Module (tSIZE) » (x) )){ x = 1 }      When( Is Zero? ( Ceil( Rand(10) Mul by(10) ) Module (tSIZE) » (y) )){ y = 1 }      At Interval [x,y], Get (table), Goto If Not Zero(__NO_VALID_POS__)   newTail=2   When ( Rand(1) Is Gt (0.9) ) { newTail=4 }   Take( newTail ), and SPut(table).   set Decimal(0)   Return\\ #context-free check Winner or Game Over   When ( Summatory ( Val( var(table) Is Gt (0) ) ), Is Eq? ( var(SIZE) Mulby(SIZE) ) ) {      MStore( 0, go, swFin ), and Back // You loose!   }   ReDim( table, 0 )  // convierte en vector   When( Scan(1, 2048, table ) Is Not Eq? (0) ){      ReDim( table, SIZE, SIZE ), Let ( go:=0 ) and Back   // You Win!   }   ReDim( table, SIZE, SIZE )   Return\\ #context-free show Table   tmpTable=0   Let ( tmpTable := CPad\$(" ", 6, Str\$(table)) )   Let ( tmpTable := Tran\$("\BGLGR\BK      \OFF","  0   ", tmpTable) )   Let ( tmpTable := Tran\$("\BGLGR\BK  2   \OFF","  2   ", tmpTable) )   Let ( tmpTable := Tran\$("\BGLGR\B  4   \OFF","  4   ", tmpTable) )   Let ( tmpTable := Tran\$("\BGLGR\B  8   \OFF","  8   ", tmpTable) )   Let ( tmpTable := Tran\$("\BGR\W  16  \OFF","  16  ", tmpTable) )   Let ( tmpTable := Tran\$("\BGY\BK  32  \OFF","  32  ", tmpTable) )   Let ( tmpTable := Tran\$("\BGB\W  64  \OFF","  64  ", tmpTable) )   Let ( tmpTable := Tran\$("\BGLM\BK 128  \OFF"," 128  ", tmpTable) )   Let ( tmpTable := Tran\$("\BGG\W 256  \OFF"," 256  ", tmpTable) )   Let ( tmpTable := Tran\$("\BGB\W 512  \OFF"," 512  ", tmpTable) )   Let ( tmpTable := Tran\$("\BGR\W 1024 \OFF"," 1024 ", tmpTable) )   Let ( tmpTable := Tran\$("\BGBK\W\ENF 2048 \OFF"," 2048 ", tmpTable) )   When( show Structure ) { print Table structure, and Let ( show Structure := 0 ) }   Clear Mark   scrx=2   For (i=1, var(i) Is Le (SIZE), ++i)        Locate in Column (scrx)        Locate in Row (2), At Interval [1,i], Print( Get (tmpTable) )        Locate in Row (4), At Interval [2,i], Print( Get (tmpTable) )        Locate in Row (6), At Interval [3,i], Print( Get (tmpTable) )        Locate in Row (8), At Interval [4,i], Print( Get (tmpTable) )        When( var(SIZE) Is Ge? (5) ) {Locate in Row (10), At Interval [5,i], Print( Get (tmpTable) )}        When( var(SIZE) Is Ge? (6) ) {Locate in Row (12), At Interval [6,i], Print( Get (tmpTable) )}        scrx += 7   Next   Clear Mark   Take( "\BGB\W\ENF","\n\n\t   2 0 4 8   \OFF\n\n",\         "Movimiento # ",contador de movimientos,", \INVSCORE=",score,"\OFF\n" )   and Print It   Return\\ #context-free print Table structure   Locate (1,1)   If ( var(SIZE) Is Eq? (4) )      Str2Utf8\$("┌──────┬──────┬──────┬──────┐\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("└──────┴──────┴──────┴──────┘")   Else If( var(SIZE) Is Eq? (5) )      Str2Utf8\$("┌──────┬──────┬──────┬──────┬──────┐\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("└──────┴──────┴──────┴──────┴──────┘")   Else If( var(SIZE) Is Eq? (6) )      Str2Utf8\$("┌──────┬──────┬──────┬──────┬──────┬──────┐\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("├──────┼──────┼──────┼──────┼──────┼──────┤\n")      Str2Utf8\$("│ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │ 2048 │\n")      Str2Utf8\$("└──────┴──────┴──────┴──────┴──────┴──────┘")   End If   now Print It   Return\\ // definicion de prototipos:#proto MovingRightDown(_X_)#proto MovingLeftUp(_X_)#proto checkPointLeftUp(_X_)#proto checkPointRightDown(_X_)#proto checkMoveRightDown(_X_)#proto checkMoveLeftUp(_X_) // definiciones varias:#define KUP        5#define KDOWN     24#define KLEFT     19#define KRIGHT     4#define KESCAPE   27#define MOVEHORZ   1#define MOVEVERT   0  // archivo de inclusión de macros H-BASIC:#include <hbasic.h> Begin  Option Ctrl+C  Option Stack 16   contador de movimientos=0  score=0, SIZE=4  When( ArgCount, Is Eq? (2) ){ get ArgNumber(2,SIZE) }   If ( var(SIZE) Is Not Between?(4,includ,6,includ) )     Print("Usage: hopper 2048.bas [4(default)-6]\n")     Stop  End If   Dim (SIZE,SIZE) for Zeros Array (table)   oldTable=0, show Structure=1 /* define initial positions */  Token Sep("")  select Position of aleatory tail  select Position of aleatory tail  Cls  Hide Cursor  show Table  c=0, go=1,swFin=1  /* game! */  While ( go )     Let ( c:=GetCh )     oldTable = table     Switch(c)        Case(KRIGHT) { _check Move Right Down(MOVEHORZ), Exit }        Case(KDOWN)  { _check Move Right Down(MOVEVERT), Exit }        Case(KLEFT)  { _check Move Left Up(MOVEHORZ),    Exit }        Case(KUP)    { _check Move Left Up(MOVEVERT),    Exit }        Case(KESCAPE){ go=0, swFin=0 }     End Switch      check Winner or Game Over     When( go ){        If ( are Not EqArray? (oldTable, table) )  //( not( equal tables(oldTable, table) ) )           select Position  of aleatory tail           ++contador de movimientos        End If        show Table     }  Wend   If ( swFin )     show Table     Print("          \LG","YOU WIN!!!\OFF")  Else     Print("          \LR","GAME OVER\OFF")  End If  Put a Newl  Show CursorEnd Subrutines Moving Right Down(tmpTab)   ht=0, sizet=0   Let( sizet := var(SIZE) Minus ( Length( Compact(tmpTab), Copy to (ht) ) ) )   If ( Is Not Zero?(sizet) )      Clear(tmpTab)      Dim (sizet) for Zeros Array (tmpTab)      Concat( ht,tmpTab )   End IfReturn (tmpTab) Moving Left Up(tmpTab)   sizet=0   Compact( tmpTab ),Clear(tmpTab) and Copy to (tmpTab); get Length It, Subtracted from (SIZE); then Move to (sizet)   //When( Is Not Zero?(sizet) ){    When( var(sizet) Is Not Zero? ){      ht=0      Dim (sizet) for Zeros Array (ht)      Concat( ht, tmpTab )   }Return (tmpTab) check Point Right Down(tmpTab)  v1=0,v2=0,tScore=0,totScore=0  For(k=SIZE, var(k) Is Gt (1), --k)     Set Interval [k] and Get (tmpTab); then Move to (v1)     Set Interval [ var(k) Minus(1)], Get(tmpTab), and Move to (v2)     If( var(v1) Is Eq? (v2) )         Add(v1,v2), Copy to(tScore);          At Interval [k] Put(tmpTab)         At Interval [ var(k) Minus(1) ], now Take(0); then Put (tmpTab)         Let( totScore := var(tScore) Plus (totScore) )     End If  Next  Take(tmpTab,totScore) and Return  check Move Right Down (_DIRECTION_)  tmpTab=0  For(i=1, while var(i) Is Le (SIZE), ++i)    If ( _DIRECTION_ )    // rows or cols??       Set Interval [i,1:SIZE]   // rows!    Else       Set Interval [1:SIZE,i]   // cols!    End If    now Let( tmpTab := Get(table) )     If( Summatory ( tmpTab ) )     // exist numbers in the row??       Clear Mark        Let( tmpTab := _Moving Right Down(tmpTab) )    // move its!       Clear Mark       Store ( _check Point Right Down(tmpTab) Plus (score), tmpTab, score )  // check score...       Clear Mark       Let ( tmpTab := _Moving Right Down(tmpTab) )   // move remanents!       If( _DIRECTION_ )          Set Interval [i,1:SIZE]       Else          Set Interval [1:SIZE,i]       End If       Take( tmpTab ), and Put(table)    End If  Next  Clear(tmpTab) and Clear Mark.Return check Point Left Up(tmpTab)  v1=0,v2=0,tScore=0,totScore=0  For(k=1, while var(k) Is Lt (SIZE),++k)     At Interval [k] Get (tmpTab), and Move to (v1)     At Interval [ var(k) Plus(1) ] Get(tmpTab), and Move to (v2)     If( var(v1) Is Eq? (v2) )        Add(v1, v2),Copy to (tScore)        At Interval [k] Put(tmpTab), At Interval [ var(k) Plus(1)]; then Take(0) and Put(tmpTab)        Let( totScore := var(tScore) Plus (totScore) )     End If  Next  Take (tmpTab,totScore)Return  check Move Left Up(_DIRECTION_)  tmpTab=0   For(i=1, while var(i) Is Le (SIZE),++i)    If( _DIRECTION_ )       Set Interval [i,1:SIZE]    Else        Set Interval [1:SIZE,i]    End If    now Get(table), and Move to (tmpTab)    If( Summatory (tmpTab) )  // exist numbers in the row??       Clear Mark       Let ( tmpTab := _Moving Left Up(tmpTab) )  // move its!       Clear Mark       Store ( _check Point Left Up(tmpTab) Plus (score), tmpTab, score )  // check score...        Clear Mark       Let ( tmpTab := _Moving Left Up(tmpTab) )  // move remanents!       If( _DIRECTION_ )          Set Interval [i,1:SIZE]       Else          Set Interval [1:SIZE,i]       End If       now Take (tmpTab), and Put(table)    End If  Next  Clear Mark.Return `

## Applesoft BASIC

`PRINT "Game 2048"  10  REM  ************ 20  REM  *   2024   * 30  REM  ************ 40  HOME  100 W = 2: REM    **** W=0 FOR LOOSE W=1 FOR WIN W=2 FOR PLAYING ****   110  DIM MA(4,4) 120 FC = 16: REM   FREECELLS  130 A\$ = "":SC = 0:MT = 2 140  GOSUB 1000: DRAW THESCREEN 150  GOSUB 1500: REM  PRINT SCORE AND MAXTILE 160  GOSUB 1700: REM  BREED 170  GOSUB 2000: REM  PRINT SCORES IN THE MATRIX AND CALC FC AND MT 200  REM  ****************** 210  REM  MAIN PROGRAM 220  REM  ****************** 230  HTAB 38: VTAB 22 235  IF W < 2 THEN  GOTO 950: REM  ******* END GAME ******** 240  WAIT  - 16384,128:A =  PEEK ( - 16384) - 128 - 68: POKE  - 16368,0 250  ON A GOTO 999,900,900,900,300,350,400,900,450 280  REM  ************************ 285  REM  FOLLOWING LINES HANDLE THE UP, LEFT, RIGHT, DOWN, NOP, EXIT 290  REM  ************************   300  GOSUB 2500: GOSUB 3500: GOSUB 2500: GOSUB 1700: GOSUB 2000: GOSUB 1     500 310  GOTO 200 350  GOSUB 2600: GOSUB 3600: GOSUB 2600: GOSUB 1700: GOSUB 2000: GOSUB 1     500 360  GOTO 200 400  GOSUB 2700: GOSUB 3700: GOSUB 2700: GOSUB 1700: GOSUB 2000: GOSUB 1     500 410  GOTO 200 450  GOSUB 2800: GOSUB 3800: GOSUB 2800: GOSUB 1700: GOSUB 2000: GOSUB 1     500 460  GOTO 200 900  GOTO 200 950  HOME : VTAB 10 960  PRINT "          ********************" 970  IF W = 1 THEN  PRINT "          *    YOU   WIN     *" 980  IF W = 0 THEN  PRINT "          *    YOU   LOOSE   *" 990  PRINT "          ********************" 995  PRINT "               SCORE  =";SC 996  PRINT "               MAXTILE=";MT 999  END  1000  REM  DRAW FRAME + SCORE 1010  FOR I = 1 TO 5 1020  VTAB 1 + (I - 1) * 4: PRINT "---------------------" 1030  NEXT I 1040  FOR I = 1 TO 4 1050  FOR J = 1 TO 3 1060  VTAB 1 + (I - 1) * 4 + J: PRINT "|    |    |    |    |" 1070  NEXT J 1080  NEXT I 1090  HTAB 30: VTAB 3: PRINT "I"; 1100  HTAB 30: VTAB 9: PRINT "M"; 1110  HTAB 25: VTAB 6: PRINT "J"; 1120  HTAB 35: VTAB 6: PRINT "K"; 1130  HTAB 25: VTAB 12: PRINT "E = END" 1140  HTAB 25: VTAB 14: PRINT "SCORE:" 1150  HTAB 25: VTAB 16: PRINT "MAXTILE:" 1160  HTAB 1: VTAB 19: PRINT "YOU CAN SLIDE  THE NUMBERS IN THE MATRIX" 1170  HTAB 1: VTAB 20: PRINT "BY PRESSING IJKM. WHEN MATCHING NUMBERS" 1180  HTAB 1: VTAB 21: PRINT "MEET THEY COMBINE IN THE SUM" 1190  HTAB 1: VTAB 22: PRINT "TO WIN YOU HAVE TO REACH THE SUM 2048" 1200  RETURN  1500  REM  *************** 1501  REM  PRINT SCORE + MAXTILE 1502  REM  *************** 1510  VTAB 14: HTAB 32: 1520 SC\$ =  STR\$ (SC):LS =  LEN (SC\$) 1530  FOR I = 1 TO 7 - LS: PRINT " ";: NEXT I 1540  PRINT SC\$ 1550  VTAB 16: HTAB 34: 1560 MT\$ =  STR\$ (MT):LS =  LEN (MT\$) 1570  FOR I = 1 TO 5 - LS: PRINT " ";: NEXT I 1580  PRINT MT\$ 1590  IF SC = 2048 THEN W = 1: REM  ******** YOU WIN ********   1690  RETURN  1700  REM  **************** 1701  REM  PUT A "2" IN A RANDOM EMPTY CELL 1702  REM  **************** 1708  IF FC = 0 THEN W = 0: GOTO 1800: REM  ***** YOU LOOSE ***** 1710 K =  INT ( RND (1) * FC + 1) 1720 N = 0 1730  FOR I = 1 TO 4 1740  FOR J = 1 TO 4 1750  IF MA(I,J) = 0 THEN N = N + 1 1760  IF N = K THEN MA(I,J) = 2:FC = FC - 1:I = 4:J = 4 1780  NEXT J 1790  NEXT I 1800  RETURN  2000  REM  ************* 2001  REM  WRITE THE CELL CONTENT AND CALC. FREECELLS AND MAXTILE 2002  REM  ************* 2005 FC = 0:MT = 0: REM   INITIALIZE FREECELLS AND MAXTILES   2010  FOR I = 1 TO 4 2020  FOR J = 1 TO 4 2030  HTAB 2 + (J - 1) * 5: VTAB 3 + (I - 1) * 4 2040  PRINT "    ";: HTAB 2 + (J - 1) * 5 2050  IF MA(I,J) = 0 THEN FC = FC + 1: GOTO 2060 2055  PRINT MA(I,J); 2060  IF MA(I,J) > MT THEN MT = MA(I,J) 2090  NEXT J 2100  NEXT I 2190  RETURN  2500  REM  ***************** 2510  REM   COMPACT UP - KIND OF BUBBLE SORT 2520  REM  ***************** 2530  FOR J = 1 TO 4 2540  FOR K = 3 TO 1 STEP  - 1 2550  FOR I = 1 TO K 2560  IF MA(I,J) = 0 THEN MA(I,J) = MA(I + 1,J):MA(I + 1,J) = 0 2570  NEXT : NEXT : NEXT  2590  RETURN  2600  REM  ************ 2610  REM  COMPACT LEFT 2620  REM  ************ 2630  FOR I = 1 TO 4 2640  FOR K = 3 TO 1 STEP  - 1 2650  FOR J = 1 TO K 2660  IF MA(I,J) = 0 THEN MA(I,J) = MA(I,J + 1):MA(I,J + 1) = 0 2670  NEXT : NEXT : NEXT  2690  RETURN  2700  REM   ************ 2710  REM   COMPACT RIGHT 2720  REM   ************ 2730  FOR I = 1 TO 4 2740  FOR K = 2 TO 4 2750  FOR J = 4 TO K STEP  - 1 2760  IF MA(I,J) = 0 THEN MA(I,J) = MA(I,J - 1):MA(I,J - 1) = 0 2770  NEXT : NEXT : NEXT  2790  RETURN  2800  REM   *****************   2810  REM    COMPACT DOWN 2820  REM   *****************   2830  FOR J = 1 TO 4 2840  FOR K = 2 TO 4 2850  FOR I = 4 TO K STEP  - 1 2860  IF MA(I,J) = 0 THEN MA(I,J) = MA(I - 1,J):MA(I - 1,J) = 0 2870  NEXT : NEXT : NEXT  2890  RETURN  3500  REM  *************** 3510  REM  ADD UP 3520  REM  *************** 3530  FOR J = 1 TO 4 3540  FOR I = 1 TO 3 3550  IF MA(I,J) = MA(I + 1,J) THEN MA(I,J) = MA(I,J) * 2:MA(I + 1,J) =      0:SC = SC + MA(I,J) 3560  NEXT : NEXT  3590  RETURN  3600  REM  ************** 3610  REM  SUM LEFT 3620  REM  ************** 3630  FOR I = 1 TO 4 3640  FOR J = 1 TO 3 3650  IF MA(I,J) = MA(I,J + 1) THEN MA(I,J) = MA(I,J) * 2:MA(I,J + 1) =      0:SC = SC + MA(I,J) 3660  NEXT : NEXT  3690  RETURN  3700  REM  ************** 3710  REM  SUM RIGHT 3720  REM  ************** 3730  FOR I = 1 TO 4 3740  FOR J = 4 TO 2 STEP  - 1 3750  IF MA(I,J) = MA(I,J - 1) THEN MA(I,J) = MA(I,J) * 2:MA(I,J - 1) =      0:SC = SC + MA(I,J) 3760  NEXT : NEXT  3790  RETURN  3800  REM   ***************  3810  REM   ADD DOWN 3820  REM   ***************   3830  FOR J = 1 TO 4 3840  FOR I = 4 TO 2 STEP  - 1 3850  IF MA(I,J) = MA(I - 1,J) THEN MA(I,J) = MA(I,J) * 2:MA(I - 1,J) =      0:SC = SC + MA(I,J) 3860  NEXT : NEXT  3890  RETURN  -----it runs somehow slowly but still fun to play. The only non standard basic is the input routine which reads directly the memory location (line 240) instead of using "input" od "get"  ---------------------|    |    |    |    ||4   |    |    |2   |        I|    |    |    |    |---------------------|    |    |    |    |   J         K|4   |2   |    |    ||    |    |    |    |---------------------        M|    |    |    |    ||2   |16  |4   |    ||    |    |    |    |   E = END---------------------|    |    |    |    |   SCORE:    4924|128 |512 |    |    ||    |    |    |    |   MAXTILE:   512--------------------- YOU CAN SLIDE  THE NUMBERS IN THE MATRIXBY PRESSING IJKM. WHEN MATCHING NUMBERSMEET THEY COMBINE IN THE SUMTO WIN YOU HAVE TO REACH THE SUM 2048   `

## ARM Assembly

Works with: as version Raspberry Pi
` /* ARM assembly Raspberry PI  *//*  program 2048.s   */  /* REMARK 1 : this program use routines in a include file    see task Include a file language arm assembly    for the routine affichageMess conversion10    see at end of this program the instruction include *//* for constantes see task include a file in arm assembly *//************************************//* Constantes                       *//************************************/.include "../constantes.inc".equ STDIN,      0    @ Linux input console.equ READ,       3    @ Linux syscall.equ SIZE,       4       .equ TOTAL,      2048.equ BUFFERSIZE, 80 .equ IOCTL,     0x36  @ Linux syscall.equ SIGACTION, 0x43  @ Linux syscall.equ SYSPOLL,   0xA8  @ Linux syscall .equ TCGETS,    0x5401.equ TCSETS,    0x5402.equ ICANON,    2.equ ECHO,     10.equ POLLIN,    1 .equ SIGINT,   2      @ Issued if the user sends an interrupt signal (Ctrl + C).equ SIGQUIT,  3      @ Issued if the user sends a quit signal (Ctrl + D).equ SIGTERM, 15      @ Software termination signal (sent by kill by default).equ SIGTTOU, 22 /*******************************************//* Structures                               *//********************************************//* structure termios see doc linux*/    .struct  0term_c_iflag:                    @ input modes    .struct  term_c_iflag + 4 term_c_oflag:                    @ output modes    .struct  term_c_oflag + 4 term_c_cflag:                    @ control modes    .struct  term_c_cflag + 4 term_c_lflag:                    @ local modes    .struct  term_c_lflag + 4 term_c_cc:                       @ special characters    .struct  term_c_cc + 20      @ see length if necessary term_fin: /* structure sigaction see doc linux */    .struct  0sa_handler:    .struct  sa_handler + 4 sa_mask:    .struct  sa_mask + 4 sa_flags:    .struct  sa_flags + 4 sa_sigaction:    .struct  sa_sigaction + 4 sa_fin: /* structure poll see doc linux */    .struct  0poll_fd:                            @   File Descriptor    .struct  poll_fd + 4 poll_events:                        @  events mask    .struct  poll_events + 4 poll_revents:                       @ events returned    .struct  poll_revents + 4 poll_fin:/*********************************//* Initialized data              *//*********************************/.dataszMessOK:           .asciz "Bravo !! You win. \n"szMessNotOK:        .asciz "You lost !! \n"szMessNewGame:      .asciz "New game (y/n) ? \n"szMessErreur:       .asciz "Error detected.\n"szCarriageReturn:   .asciz "\n"//szMessMovePos:             .asciz "\033[00;00H"szMess0:            .asciz "      "szMess2:            .asciz "   2  "szMess4:            .asciz "   4  "szMess8:            .asciz "   8  "szMess16:           .asciz "  16  "szMess32:           .asciz "  32  "szMess64:           .asciz "  64  "szMess128:          .asciz " 128  "szMess256:          .asciz " 256  "szMess512:          .asciz " 512  "szMess1024:         .asciz " 1024 "szMess2048:         .asciz " 2048 "szClear1:           .byte 0x1B                     .byte 'c'           @ other console clear                    .byte 0 szLineH:            .asciz "-----------------------------\n"szLineV:            .asciz "|"szLineVT:           .asciz "|      |      |      |      |\n".align 4iGraine:            .int 123456/*********************************//* UnInitialized data            *//*********************************/.bss.align 4sZoneConv:      .skip 24sBuffer:        .skip BUFFERSIZEiTbCase:        .skip 4 * SIZE * SIZEiEnd:           .skip 4                        @ 0 loop  1 = end loopiTouche:        .skip 4                        @ value key pressedstOldtio:       .skip term_fin                 @ old terminal statestCurtio:       .skip term_fin                 @ current terminal statestSigAction:    .skip sa_fin                   @ area signal structurestSigAction1:   .skip sa_finstPoll1:        .skip poll_fin                 @ area poll structurestPoll2:        .skip poll_fin/*********************************//*  code section                 *//*********************************/.text.global main main:                                 @ entry of program  1:                                    @ begin game loop    ldr r0,iAdrszClear1    bl affichageMess    bl razTable2:    bl addDigit       cmp r0,#-1    beq 5f                            @ end game    bl displayGame3:    bl readKey    cmp r0,#-1    beq 100f                          @ error or control-c    bl keyMove    cmp r0,#0    beq 3b                            @ no change -> loop    cmp r0,#2                         @ last addition = 2048 ?    beq 4f    cmp r0,#-1                        @ quit ?    bne 2b                            @ loop     b 10f4:                                    @ last addition = 2048     ldr r0,iAdrszMessOK    bl affichageMess    b 10f5:                                    @ display message no solution    ldr r0,iAdrszMessNotOK    bl affichageMess 10:                                   @ display new game ?    ldr r0,iAdrszCarriageReturn    bl affichageMess    ldr r0,iAdrszMessNewGame    bl affichageMess    bl readKey    ldr r0,iAdriTouche    ldrb r0,[r0]    cmp r0,#'y'    beq 1b    cmp r0,#'Y'    beq 1b 100:                                  @ standard end of the program     mov r0, #0                        @ return code    mov r7, #EXIT                     @ request to exit program    svc #0                            @ perform the system call iAdrszCarriageReturn:     .int szCarriageReturniAdrszMessNotOK:          .int szMessNotOKiAdrszMessOK:             .int szMessOKiAdrszMessNewGame:        .int szMessNewGameiAdrsZoneConv:            .int sZoneConviAdrszClear1:             .int szClear1/******************************************************************//*     raz table cases                                                   */ /******************************************************************/razTable:    push {r0-r2,lr}                  @ save  registers    ldr r1,iAdriTbCase    mov r0,#0    mov r2,#01:    str r0,[r1,r2,lsl #2]    add r2,r2,#1    cmp r2,#SIZE * SIZE    blt 1b100:    pop {r0-r2,lr}                   @ restaur registers     bx lr                            @return/******************************************************************//*     key move                                                   */ /******************************************************************//* r0 contains key value               */keyMove:    push {r1,lr}                  @ save  registers    cmp r0,#0x42                  @ down arrow     bne 1f    bl moveDown    b 100f1:    cmp r0,#0x41                  @ high arrow    bne 2f    bl moveUp    b 100f2:    cmp r0,#0x43                  @ right arrow    bne 3f    bl moveRight    b 100f3:    cmp r0,#0x44                  @ left arrow    bne 4f    bl moveLeft    b 100f4:    ldr r0,iAdriTouche    ldrb r0,[r0]    cmp r0,#'q'                   @ quit game    bne 5f    mov r0,#-1    b 100f5:    cmp r0,#'Q'                   @ quit game    bne 100f    mov r0,#-1    b 100f 100:    pop {r1,lr}                   @ restaur registers     bx lr                         @return/******************************************************************//*           move left                                   */ /******************************************************************//* r0 return -1 if ok     */moveLeft:    push {r1-r10,lr}            @ save registers    ldr r1,iAdriTbCase    mov r0,#0                   @ top move Ok    mov r2,#0                   @ line indice1:    mov r6,#0                   @ counter empty case    mov r7,#0                   @ first digit    mov r10,#0                  @ last digit to add    mov r3,#0                   @ column indice2:    lsl r5,r2,#2                @ change this if size <> 4    add r5,r5,r3                @ compute table indice    ldr r4,[r1,r5,lsl #2]    cmp r4,#0    addeq r6,r6,#1              @ positions vides    beq 5f    cmp r6,#0    beq 3f                      @ no empty left case    mov r8,#0    str r8,[r1,r5,lsl #2]       @ raz digit    sub r5,r5,r6    str r4,[r1,r5,lsl #2]       @ and store to left empty position    mov r0,#1                   @ move Ok    //sub r6,r6,#13:    cmp r7,#0                   @ first digit    beq 4f    cmp r10,r4                  @ prec digit have to add     beq 4f    sub r8,r5,#1                @ prec digit     ldr r9,[r1,r8,lsl #2]    cmp r4,r9                   @ equal ?    bne 4f    mov r10,r4                  @ save digit     add r4,r4,r9                @ yes -> add    str r4,[r1,r8,lsl #2]    cmp r4,#TOTAL    moveq r0,#2    beq 100f    mov r4,#0    str r4,[r1,r5,lsl #2]    add r6,r6,#1                @ empty case + 1    mov r0,#1                   @ move Ok4:    add r7,r7,#1                @ no first digit 5:                              @ and loop    add r3,r3,#1    cmp r3,#SIZE    blt 2b    add r2,r2,#1    cmp r2,#SIZE    blt 1b100:    pop {r1-r12,lr}    bx lr                       @ return /******************************************************************//*           move right                                   */ /******************************************************************//* r0 return -1 if ok     */moveRight:    push {r1-r5,lr}                @ save registers    ldr r1,iAdriTbCase    mov r0,#0    mov r2,#01:    mov r6,#0    mov r7,#0    mov r10,#0    mov r3,#SIZE-12:    lsl r5,r2,#2                  @ change this if size <> 4    add r5,r5,r3    ldr r4,[r1,r5,lsl #2]    cmp r4,#0    addeq r6,r6,#1                @ positions vides    beq 5f     cmp r6,#0    beq 3f                      @ no empty right case    mov r0,#0    str r0,[r1,r5,lsl #2]       @ raz digit    add r5,r5,r6    str r4,[r1,r5,lsl #2]       @ and store to right empty position    mov r0,#13:    cmp r7,#0                   @ first digit    beq 4f    add r8,r5,#1                @ next digit     ldr r9,[r1,r8,lsl #2]    cmp r4,r9                   @ equal ?    bne 4f    cmp r10,r4    beq 4f    mov r10,r4    add r4,r4,r9                @ yes -> add    str r4,[r1,r8,lsl #2]    cmp r4,#TOTAL    moveq r0,#2    beq 100f    mov r4,#0    str r4,[r1,r5,lsl #2]    add r6,r6,#1                @ empty case + 1    mov r0,#14:    add r7,r7,#1                @ no first digit 5:                              @ and loop    sub r3,r3,#1    cmp r3,#0    bge 2b    add r2,r2,#1    cmp r2,#SIZE    blt 1b 100:    pop {r1-r5,lr}    bx lr                          @ return /******************************************************************//*           move down                                   */ /******************************************************************//* r0 return -1 if ok     */moveDown:    push {r1-r5,lr}                @ save registers    ldr r1,iAdriTbCase    mov r0,#0    mov r3,#01:    mov r6,#0    mov r7,#0    mov r10,#0    mov r2,#SIZE-12:    lsl r5,r2,#2                  @ change this if size <> 4    add r5,r5,r3    ldr r4,[r1,r5,lsl #2]    cmp r4,#0    addeq r6,r6,#1                @ positions vides    beq 5f    cmp r6,#0    beq 3f                      @ no empty right case    mov r0,#0    str r0,[r1,r5,lsl #2]       @ raz digit    lsl r0,r6,#2    add r5,r5,r0    str r4,[r1,r5,lsl #2]       @ and store to right empty position    mov r0,#13:    cmp r7,#0                   @ first digit    beq 4f    add r8,r5,#SIZE                @ down digit     ldr r9,[r1,r8,lsl #2]    cmp r4,r9                   @ equal ?    bne 4f    cmp r10,r4    beq 4f    mov r10,r4    add r4,r4,r9                @ yes -> add    str r4,[r1,r8,lsl #2]    cmp r4,#TOTAL    moveq r0,#2    beq 100f    mov r4,#0    str r4,[r1,r5,lsl #2]    add r6,r6,#1                @ empty case + 1    mov r0,#14:    add r7,r7,#1                   @ no first digit 5:                           @ and loop    sub r2,r2,#1    cmp r2,#0    bge 2b    add r3,r3,#1    cmp r3,#SIZE    blt 1b 100:    pop {r1-r5,lr}    bx lr                          @ return /******************************************************************//*           move up                                   */ /******************************************************************//* r0 return -1 if ok     */moveUp:    push {r1-r5,lr}                @ save registers    ldr r1,iAdriTbCase    mov r0,#0    mov r3,#01:    mov r6,#0    mov r7,#0    mov r10,#0    mov r2,#02:    lsl r5,r2,#2                  @ change this if size <> 4    add r5,r5,r3    ldr r4,[r1,r5,lsl #2]    cmp r4,#0    addeq r6,r6,#1                @ positions vides    beq 5f    cmp r6,#0    beq 3f                      @ no empty right case    mov r0,#0    str r0,[r1,r5,lsl #2]       @ raz digit    lsl r0,r6,#2    sub r5,r5,r0    str r4,[r1,r5,lsl #2]       @ and store to right empty position    mov r0,#13:    cmp r7,#0                   @ first digit    beq 4f    sub r8,r5,#SIZE             @ up digit     ldr r9,[r1,r8,lsl #2]    cmp r4,r9                   @ equal ?    bne 4f    cmp r10,r4    beq 4f    mov r10,r4    add r4,r4,r9                @ yes -> add    str r4,[r1,r8,lsl #2]    cmp r4,#TOTAL    moveq r0,#2    beq 100f    mov r4,#0    str r4,[r1,r5,lsl #2]    add r6,r6,#1                @ empty case + 1    mov r0,#14:    add r7,r7,#1                @ no first digit 5:                              @ and loop    add r2,r2,#1    cmp r2,#SIZE    blt 2b    add r3,r3,#1    cmp r3,#SIZE    blt 1b 100:    pop {r1-r5,lr}    bx lr                          @ return /******************************************************************//*           add new digit on game                                   */ /******************************************************************//* r0 return -1 if ok     */addDigit:    push {r1-r5,lr}                @ save registers    sub sp,#4 * SIZE*SIZE    mov fp,sp     mov r0,#100    bl genereraleas    cmp r0,#10    movlt r5,#4    movge r5,#2    ldr r1,iAdriTbCase    mov r3,#0    mov r4,#01:      ldr r2,[r1,r3,lsl #2]    cmp r2,#0    bne 2f    str r3,[fp,r4,lsl #2]    add r4,r4,#12:    add r3,r3,#1    cmp r3,#SIZE*SIZE    blt 1b    cmp r4,#0              @ no empty case    moveq r0,#-1    beq 100f    cmp r4,#1                 bne 3f    ldr r2,[fp]            @ one case    str r5,[r1,r2,lsl #2]    mov r0,#0    b 100f3:                         @ multiple case    sub r0,r4,#1    bl genereraleas    ldr r2,[fp,r0,lsl #2]    str r5,[r1,r2,lsl #2]    mov r0,#0 100:    add sp,#4*  (SIZE*SIZE)    @ stack alignement    pop {r1-r5,lr}    bx lr                      @ return iAdriTbCase:         .int iTbCase/******************************************************************//*            display game                                      */ /******************************************************************/displayGame:    push {r1-r3,lr}            @ save registers    ldr r0,iAdrszClear1    bl affichageMess    ldr r0,iAdrszLineH    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess    ldr r1,iAdriTbCase    mov r2,#01:    ldr r0,[r1,r2,lsl #2]    bl digitString    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess    add r2,r2,#1    cmp r2,#SIZE    blt 1b    ldr r0,iAdrszCarriageReturn    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineH    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess2:    ldr r0,[r1,r2,lsl #2]    bl digitString    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess    add r2,r2,#1    cmp r2,#SIZE*2    blt 2b    ldr r0,iAdrszCarriageReturn    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineH    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess3:    ldr r0,[r1,r2,lsl #2]    bl digitString    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess    add r2,r2,#1    cmp r2,#SIZE*3    blt 3b    ldr r0,iAdrszCarriageReturn    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineH    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess4:    ldr r0,[r1,r2,lsl #2]    bl digitString    bl affichageMess    ldr r0,iAdrszLineV    bl affichageMess    add r2,r2,#1    cmp r2,#SIZE*4    blt 4b    ldr r0,iAdrszCarriageReturn    bl affichageMess    ldr r0,iAdrszLineVT    bl affichageMess    ldr r0,iAdrszLineH    bl affichageMess 100:    pop {r1-r3,lr}    bx lr                       @ return iAdrszLineH:         .int szLineHiAdrszLineV:         .int szLineViAdrszLineVT:        .int szLineVT//iAdrszMessMovePos:   .int szMessMovePos/******************************************************************//*            digits string                                       */ /******************************************************************//* r0 contains number *//* r0 return address string */digitString:    push {r1,lr}        @ save registers    cmp r0,#0    bne 1f    ldr r0,iAdrszMess0    b 100f1:    cmp r0,#2    bne 2f    ldr r0,iAdrszMess2    b 100f2:    cmp r0,#4    bne 3f    ldr r0,iAdrszMess4    b 100f3:    cmp r0,#8    bne 4f    ldr r0,iAdrszMess8    b 100f4:    cmp r0,#16    bne 5f    ldr r0,iAdrszMess16    b 100f5:    cmp r0,#32    bne 6f    ldr r0,iAdrszMess32    b 100f6:    cmp r0,#64    bne 7f    ldr r0,iAdrszMess64    b 100f7:    cmp r0,#128    bne 8f    ldr r0,iAdrszMess128    b 100f8:    cmp r0,#256    bne 9f    ldr r0,iAdrszMess256    b 100f9:    cmp r0,#512    bne 10f    ldr r0,iAdrszMess512    b 100f10:    cmp r0,#1024    bne 11f    ldr r0,iAdrszMess1024    b 100f11:    cmp r0,#2048    bne 12f    ldr r0,iAdrszMess2048    b 100f12:    ldr r1,iAdrszMessErreur                       @ error message    bl   displayError100:    pop {r1,lr}    bx lr                   @ return iAdrszMess0:          .int szMess0iAdrszMess2:          .int szMess2iAdrszMess4:          .int szMess4iAdrszMess8:          .int szMess8iAdrszMess16:         .int szMess16iAdrszMess32:         .int szMess32iAdrszMess64:         .int szMess64iAdrszMess128:        .int szMess128iAdrszMess256:        .int szMess256iAdrszMess512:        .int szMess512iAdrszMess1024:       .int szMess1024iAdrszMess2048:        .int szMess2048 //iAdrsBuffer:         .int sBuffer/***************************************************//*   Generation random number                  *//***************************************************//* r0 contains limit  */genereraleas:    push {r1-r4,lr}         @ save registers     ldr r4,iAdriGraine    ldr r2,[r4]    ldr r3,iNbDep1    mul r2,r3,r2    ldr r3,iNbDep2    add r2,r2,r3    str r2,[r4]             @ maj de la graine pour l appel suivant     cmp r0,#0    beq 100f    add r1,r0,#1            @ divisor    mov r0,r2               @ dividende    bl division    mov r0,r3               @ résult = remainder 100:                        @ end function    pop {r1-r4,lr}          @ restaur registers    bx lr                   @ return/*****************************************************/iAdriGraine: .int iGraineiNbDep1:     .int 0x343FDiNbDep2:     .int 0x269EC3 /***************************************************//* read touch                                      *//***************************************************/readKey:    push {r1-r7,lr}    mov r5,#0    ldr r1,iAdriTouche                            @ buffer address    str r5,[r1]                                  @ raz 4 bytes iTouche    /* read terminal state */    mov r0,#STDIN                                @ input console    mov r1,#TCGETS    ldr r2,iAdrstOldtio    mov r7, #IOCTL                               @ call system Linux    svc #0     cmp r0,#0                                    @ error ?    beq 1f    ldr r1,iAdrszMessErreur                      @ error message    bl   displayError    mov r0,#-1    b 100f1:    adr r0,sighandler                            @ adresse routine traitement signal    ldr r1,iAdrstSigAction                       @ adresse structure sigaction    str r0,[r1,#sa_handler]                      @ maj handler    mov r0,#SIGINT                               @ signal type    ldr r1,iAdrstSigAction    mov r2,#0                                    @ NULL    mov r7, #SIGACTION                           @ call system    svc #0     cmp r0,#0                                    @ error ?    bne 97f    mov r0,#SIGQUIT    ldr r1,iAdrstSigAction    mov r2,#0                                    @ NULL    mov r7, #SIGACTION                           @ call system     svc #0     cmp r0,#0                                    @ error ?    bne 97f    mov r0,#SIGTERM    ldr r1,iAdrstSigAction    mov r2,#0                                    @ NULL    mov r7, #SIGACTION                           @ appel systeme     svc #0     cmp r0,#0    bne 97f    @    adr r0,iSIG_IGN                              @ address signal ignore function    ldr r1,iAdrstSigAction1    str r0,[r1,#sa_handler]    mov r0,#SIGTTOU                              @invalidate other process signal    ldr r1,iAdrstSigAction1    mov r2,#0                                    @ NULL    mov r7,#SIGACTION                            @ call system     svc #0     cmp r0,#0    bne 97f    @    /* read terminal current state  */    mov r0,#STDIN    mov r1,#TCGETS    ldr r2,iAdrstCurtio                          @ address current termio    mov r7,#IOCTL                                @ call systeme     svc #0     cmp r0,#0                                    @ error ?    bne 97f    mov r2,#ICANON | ECHO                        @ no key pressed echo on display    mvn r2,r2                                    @ and one key     ldr r1,iAdrstCurtio    ldr r3,[r1,#term_c_lflag]    and r3,r2                                    @ add flags     str r3,[r1,#term_c_lflag]                    @ and store    mov r0,#STDIN                                @ maj terminal current state     mov r1,#TCSETS    ldr r2,iAdrstCurtio    mov r7, #IOCTL                               @ call system    svc #0     cmp r0,#0    bne 97f    @2:                                               @ loop waiting key    ldr r0,iAdriEnd                              @ if signal ctrl-c  -> end    ldr r0,[r0]    cmp r0,#0    movne r5,#-1    bne 98f    ldr r0,iAdrstPoll1                            @ address structure poll    mov r1,#STDIN    str r1,[r0,#poll_fd]                          @ maj FD    mov r1,#POLLIN                                @ action code    str r1,[r0,#poll_events]    mov r1,#1                                     @ items number structure poll    mov r2,#0                                     @ timeout = 0     mov r7,#SYSPOLL                               @ call system POLL    svc #0     cmp r0,#0                                     @ key pressed ?    ble 2b                                        @ no key pressed -> loop                                                  @ read key    mov r0,#STDIN                                 @ File Descriptor    ldr r1,iAdriTouche                            @ buffer address    mov r2,#BUFFERSIZE                            @ buffer size    mov r7,#READ                                  @ read key    svc #0    cmp r0,#0                                     @ error ?    bgt 98f 97:                                               @ error detected    ldr r1,iAdrszMessErreur                       @ error message    bl   displayError    mov r5,#-198:                                               @ end then restaur begin state terminal    mov r0,#STDIN    mov r1,#TCSETS    ldr r2,iAdrstOldtio    mov r7,#IOCTL                                 @ call system      svc #0    cmp r0,#0    beq 99f                                       @ restaur ok    ldr r1,iAdrszMessErreur                       @ error message    bl   displayError    mov r0,#-1    b 100f99:    cmp r5,#0                                     @ no error or control-c ?    ldreq r2,iAdriTouche                          @ key address    ldreqb r0,[r2,#2]                             @ return key byte    movne r0,r5                                   @ or error100:    pop {r1-r7, lr}    bx lriSIG_IGN:                 .int 1iAdriEnd:                 .int iEndiAdrstPoll1:              .int stPoll1iAdriTouche:              .int iToucheiAdrstOldtio:             .int stOldtioiAdrstCurtio:             .int stCurtioiAdrstSigAction:          .int stSigActioniAdrstSigAction1:         .int stSigAction1iAdrszMessErreur :        .int szMessErreur /******************************************************************//*     traitement du signal                                       */ /******************************************************************/sighandler:    push {r0,r1}    ldr r0,iAdriEnd    mov r1,#1                 @ maj zone end    str r1,[r0]    pop {r0,r1}    bx lr/***************************************************//*      ROUTINES INCLUDE                           *//***************************************************/.include "../affichage.inc" `
Output:
```-----------------------------
|      |      |      |      |
|   8  |   4  |   4  |   2  |
|      |      |      |      |
-----------------------------
|      |      |      |      |
|  64  |  16  |      |      |
|      |      |      |      |
-----------------------------
|      |      |      |      |
|  16  |      |      |   2  |
|      |      |      |      |
-----------------------------
|      |      |      |      |
|   2  |      |      |      |
|      |      |      |      |
-----------------------------
```

## 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:: Batch File Implementation v2.0.1 @echo offsetlocal enabledelayedexpansionrem initialization:begin_gameset "size=4"   %== board size ==%set "score=0"   %== current score ==%set "won=0"   %== boolean for winning ==%set "target=2048"   %== as the game title says ==%for /l %%R in (1,1,%size%) do for /l %%C in (1,1,%size%) do set "X_%%R_%%C=0"rem add two numbers in the boardcall :addtilecall :addtilerem main game loop:main_loopcall :displayecho(echo(Keys: WASD (Slide Movement), N (New game), P (Exit)rem get keypress trickset "key="for /f "delims=" %%? in ('xcopy /w "%~f0" "%~f0" 2^>nul') do if not defined key set "key=%%?"set "key=%key:~-1%" set "changed=0"   %== boolean for changed board ==%set "valid_key=0"   %== boolean for pressing WASD ==%rem process keypressif /i "!key!" equ "W" (set "valid_key=1" & call :slide "C" "1,1,%size%" "X")if /i "!key!" equ "A" (set "valid_key=1" & call :slide "R" "1,1,%size%" "X")if /i "!key!" equ "S" (set "valid_key=1" & call :slide "C" "%size%,-1,1" "X")if /i "!key!" equ "D" (set "valid_key=1" & call :slide "R" "%size%,-1,1" "X")if /i "!key!" equ "N" goto begin_gameif /i "!key!" equ "P" exit /b 0if "%valid_key%" equ "0" goto main_looprem check if the board changedif %changed% neq 0 call :addtilerem check for win conditionif %won% equ 1 (    set "msg=Nice one... You WON^!^!"    goto gameover)rem check for lose conditionif %blank_count% equ 0 (    for /l %%R in (1,1,%size%) do for /l %%C in (1,1,%size%) do set "LX_%%R_%%C=!X_%%R_%%C!"    set "save_changed=%changed%" & set "changed=0"   %== save actual changed for test ==%    call :slide "C" "1,1,%size%" "LX"    call :slide "R" "1,1,%size%" "LX"    if !changed! equ 0 (        set "msg=No moves are possible... Game Over :("        goto gameover    ) else set "changed=!save_changed!")goto main_looprem add number to a random blank tile:addtileset "blank_count=0"   %== blank tile counter ==%set "new_tile="   %== clearing ==%rem create pseudo-array blank_tilesfor /l %%R in (1,1,%size%) do (    for /l %%C in (1,1,%size%) do (        if !X_%%R_%%C! equ 0 (            set "blank_tiles[!blank_count!]=X_%%R_%%C"            set /a "blank_count+=1"        )    ))if %blank_count% equ 0 goto :EOFset /a "pick_tile=%random%%%%blank_count%"set "new_tile=!blank_tiles[%pick_tile%]!"set /a "rnd_newnum=%random%%%10"rem 10% chance new number is 4, 90% chance it's 2if %rnd_newnum% equ 5 (set "%new_tile%=4") else (set "%new_tile%=2")set /a "blank_count-=1"   %== to be used for checking lose condition ==%goto :EOFrem display the board:displayclsecho(2048 Game in Batchecho(set "wall=+"for /l %%C in (1,1,%size%) do set "wall=!wall!----+"for /l %%R in (1,1,%size%) do (    set "disp_row=|"    for /l %%C in (1,1,%size%) do (        if "!new_tile!" equ "X_%%R_%%C" (set "DX_%%R_%%C=  +!X_%%R_%%C!") else (            set "DX_%%R_%%C=!X_%%R_%%C!"             if !X_%%R_%%C! lss 1000 set "DX_%%R_%%C= !DX_%%R_%%C!"            if !X_%%R_%%C! lss 100 set "DX_%%R_%%C= !DX_%%R_%%C!"            if !X_%%R_%%C! lss 10 set "DX_%%R_%%C= !DX_%%R_%%C!"            if !X_%%R_%%C! equ 0 set "DX_%%R_%%C=    "        )        set "disp_row=!disp_row!!DX_%%R_%%C!|"    )    echo(%wall%    echo(!disp_row!)echo(%wall%echo(echo(Score: %score%goto :EOFrem the main slider of numbers in tiles:sliderem %%A and %%B are used here because sliding direction is variablefor /l %%A in (1,1,%size%) do (    rem first slide: removing blank tiles in the middle    set "slide_1="    set "last_blank=0"   %== boolean if last tile is blank ==%    for /l %%B in (%~2) do (        if "%~1" equ "R" (set "curr_tilenum=!%~3_%%A_%%B!"        ) else if "%~1" equ "C" (set "curr_tilenum=!%~3_%%B_%%A!")        if !curr_tilenum! equ 0 (set "last_blank=1") else (            set "slide_1=!slide_1! !curr_tilenum!"            if !last_blank! equ 1 set "changed=1"            set "last_blank=0"        )    )    rem second slide: addition of numbered tiles    rem slide_2 would be pseudo-array    set "slide_2_count=0"    set "skip=1"   %== boolean for skipping after previous summing ==%    if "!slide_1!" neq "" for %%S in (!slide_1! 0) do (        if !skip! equ 1 (            set "prev_tilenum=%%S" & set "skip=0"        ) else if !skip! equ 0 (            if %%S equ !prev_tilenum! (                set /a "sum=%%S+!prev_tilenum!"                if "%~3" equ "X" set /a "score+=sum"                set "changed=1" & set "skip=1"                rem check for winning condition!                if !sum! equ !target! set "won=1"            ) else (                set "sum=!prev_tilenum!"                set "prev_tilenum=%%S"            )            set "slide_2[!slide_2_count!]=!sum!"            set /a "slide_2_count+=1"        )    )    rem new values of tiles    set "slide_2_run=0"   %== running counter for slide_2 ==%    for /l %%B in (%~2) do (        if "%~1" equ "R" (set "curr_tile=%~3_%%A_%%B"        ) else if "%~1" equ "C" (set "curr_tile=%~3_%%B_%%A")        for %%? in ("!slide_2_run!") do (            if %%~? lss !slide_2_count! (set "!curr_tile!=!slide_2[%%~?]!"            ) else (set "!curr_tile!=0")        )        set /a "slide_2_run+=1"    ))goto :EOFrem game over xD:gameovercall :displayecho(echo(!msg!echo(echo(Press any key to exit . . .pause>nulexit /b 0`
Output:
```2048 Game in Batch

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

Score: 60

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

## BASIC

Works with: QBasic

El código es de MichD (https://github.com/michd/2048-qbasic)

Yo solo lo transcribo.

` SCREEN 13PALETTE 1, pColor(35, 33, 31)PALETTE 2, pColor(46, 46, 51)PALETTE 3, pColor(59, 56, 50)PALETTE 4, pColor(61, 44, 30)PALETTE 5, pColor(61, 37, 25)PALETTE 6, pColor(62, 31, 24)PALETTE 7, pColor(62, 24, 15)PALETTE 8, pColor(59, 52, 29)PALETTE 9, pColor(59, 51, 24)PALETTE 10, pColor(59, 50, 20)PALETTE 11, pColor(59, 49, 16)PALETTE 12, pColor(59, 49, 12)PALETTE 13, pColor(15, 15, 13)PALETTE 14, pColor(23, 22, 20) DIM SHARED gDebugDIM SHARED gOriginXDIM SHARED gOriginYDIM SHARED gTextOriginXDIM SHARED gTextOriginYDIM SHARED gSquareSideDIM SHARED gGridSize gGridSize = 4  ' grid size (4 -> 4x4) DIM SHARED gGrid(gGridSize, gGridSize)DIM SHARED gScore ' Don't touch these numbers, seriously gOriginX = 75 'pixel X of top left of gridgOriginY = 12 'pixel Y of top right of gridgTextOriginX = 11gTextOriginY = 3gSquareSide = 38 'width/height of block in pixels  'set up all the things!gDebug = 0 RANDOMIZE TIMERCLS start:initGridinitGraphicGridrenderGridupdateScore gScore = 0 LOCATE 23, 1PRINT "Move with arrow keys. (R)estart, (Q)uit" ' keyboard input loopDO  DO    k\$ = INKEY\$  LOOP UNTIL k\$ <> ""   SELECT CASE k\$    CASE CHR\$(0) + CHR\$(72) 'up      processMove ("u")    CASE CHR\$(0) + CHR\$(80) 'down      processMove ("d")    CASE CHR\$(0) + CHR\$(77) 'right      processMove ("r")    CASE CHR\$(0) + CHR\$(75) 'left      processMove ("l")    CASE CHR\$(27)           'escape      GOTO programEnd    CASE "q"      GOTO programEnd    CASE "Q"      GOTO programEnd    CASE "r"      GOTO start    CASE "R"      GOTO start  END SELECTLOOP programEnd: SUB addblock  DIM emptyCells(gGridSize * gGridSize, 2)  emptyCellCount = 0   FOR x = 0 TO gGridSize - 1    FOR y = 0 TO gGridSize - 1      IF gGrid(x, y) = 0 THEN        emptyCells(emptyCellCount, 0) = x        emptyCells(emptyCellCount, 1) = y        emptyCellCount = emptyCellCount + 1      END IF    NEXT y  NEXT x   IF emptyCellCount > 0 THEN    index = INT(RND * emptyCellCount)    num = CINT(RND + 1) * 2    gGrid(emptyCells(index, 0), emptyCells(index, 1)) = num  END IF END SUB SUB drawNumber (num, xPos, yPos)  SELECT CASE num    CASE 0:    c = 16    CASE 2:    c = 2    CASE 4:    c = 3    CASE 8:    c = 4    CASE 16:   c = 5    CASE 32:   c = 6    CASE 64:   c = 7    CASE 128:  c = 8    CASE 256:  c = 9    CASE 512:  c = 10    CASE 1024: c = 11    CASE 2048: c = 12    CASE 4096: c = 13    CASE 8192: c = 13    CASE ELSE: c = 13  END SELECT   x = xPos * (gSquareSide + 2) + gOriginX + 1  y = yPos * (gSquareSide + 2) + gOriginY + 1  LINE (x + 1, y + 1)-(x + gSquareSide - 1, y + gSquareSide - 1), c, BF   IF num > 0 THEN    LOCATE gTextOriginY + 1 + (yPos * 5), gTextOriginX + (xPos * 5)    PRINT "    "    LOCATE gTextOriginY + 2 + (yPos * 5), gTextOriginX + (xPos * 5)    PRINT pad\$(num)    LOCATE gTextOriginY + 3 + (yPos * 5), gTextOriginX + (xPos * 5)    'PRINT "    "  END IF END SUB FUNCTION getAdjacentCell (x, y, d AS STRING)   IF (d = "l" AND x = 0) OR (d = "r" AND x = gGridSize - 1) OR (d = "u" AND y = 0) OR (d = "d" AND y = gGridSize - 1) THEN    getAdjacentCell = -1  ELSE    SELECT CASE d      CASE "l": getAdjacentCell = gGrid(x - 1, y)      CASE "r": getAdjacentCell = gGrid(x + 1, y)       CASE "u": getAdjacentCell = gGrid(x, y - 1)      CASE "d": getAdjacentCell = gGrid(x, y + 1)    END SELECT  END IF END FUNCTION 'Draws the outside grid (doesn't render tiles)SUB initGraphicGrid   gridSide = (gSquareSide + 2) * gGridSize   LINE (gOriginX, gOriginY)-(gOriginX + gridSide, gOriginY + gridSide), 14, BF 'outer square, 3 thick  LINE (gOriginX, gOriginY)-(gOriginX + gridSide, gOriginY + gridSide), 1, B 'outer square, 3 thick  LINE (gOriginX - 1, gOriginY - 1)-(gOriginX + gridSide + 1, gOriginY + gridSide + 1), 1, B  LINE (gOriginX - 2, gOriginY - 2)-(gOriginX + gridSide + 2, gOriginY + gridSide + 2), 1, B   FOR x = gOriginX + gSquareSide + 2 TO gOriginX + (gSquareSide + 2) * gGridSize STEP gSquareSide + 2  ' horizontal lines    LINE (x, gOriginY)-(x, gOriginY + gridSide), 1  NEXT x   FOR y = gOriginY + gSquareSide + 2 TO gOriginY + (gSquareSide + 2) * gGridSize STEP gSquareSide + 2 ' vertical lines    LINE (gOriginX, y)-(gOriginX + gridSide, y), 1  NEXT y END SUB 'Init the (data) grid with 0sSUB initGrid  FOR x = 0 TO 3    FOR y = 0 TO 3      gGrid(x, y) = 0    NEXT y  NEXT x   addblock  addblock END SUB SUB moveBlock (sourceX, sourceY, targetX, targetY, merge)   IF sourceX < 0 OR sourceX >= gGridSize OR sourceY < 0 OR sourceY >= gGridSize AND gDebug = 1 THEN    LOCATE 0, 0    PRINT "moveBlock: source coords out of bounds"  END IF   IF targetX < 0 OR targetX >= gGridSize OR targetY < 0 OR targetY >= gGridSize AND gDebug = 1 THEN    LOCATE 0, 0    PRINT "moveBlock: source coords out of bounds"  END IF   sourceSquareValue = gGrid(sourceX, sourceY)  targetSquareValue = gGrid(targetX, targetY)   IF merge = 1 THEN     IF sourceSquareValue = targetSquareValue THEN      gGrid(sourceX, sourceY) = 0      gGrid(targetX, targetY) = targetSquareValue * 2      gScore = gScore + targetSquareValue * 2 ' Points!    ELSEIF gDebug = 1 THEN      LOCATE 0, 0      PRINT "moveBlock: Attempted to merge unequal sqs"    END IF   ELSE     IF targetSquareValue = 0 THEN      gGrid(sourceX, sourceY) = 0      gGrid(targetX, targetY) = sourceSquareValue    ELSEIF gDebug = 1 THEN      LOCATE 0, 0      PRINT "moveBlock: Attempted to move to non-empty block"    END IF   END IF END SUB FUNCTION pad\$ (num)  strNum\$ = LTRIM\$(STR\$(num))   SELECT CASE LEN(strNum\$)    CASE 1: pad = "  " + strNum\$ + " "    CASE 2: pad = " " + strNum\$ + " "    CASE 3: pad = " " + strNum\$    CASE 4: pad = strNum\$  END SELECT END FUNCTION FUNCTION pColor (r, g, b)  pColor = (r + g * 256 + b * 65536)END FUNCTION SUB processMove (dir AS STRING)  ' dir can be 'l', 'r', 'u', or 'd'   hasMoved = 0   IF dir = "l" THEN     FOR y = 0 TO gGridSize - 1      wasMerge = 0      FOR x = 0 TO gGridSize - 1        GOSUB processBlock      NEXT x    NEXT y   ELSEIF dir = "r" THEN     FOR y = 0 TO gGridSize - 1      wasMerge = 0      FOR x = gGridSize - 1 TO 0 STEP -1        GOSUB processBlock      NEXT x    NEXT y   ELSEIF dir = "u" THEN    FOR x = 0 TO gGridSize - 1      wasMerge = 0      FOR y = 0 TO gGridSize - 1        GOSUB processBlock      NEXT y    NEXT x   ELSEIF dir = "d" THEN    FOR x = 0 TO gGridSize - 1      wasMerge = 0      FOR y = gGridSize - 1 TO 0 STEP -1        GOSUB processBlock      NEXT y    NEXT x   END IF   GOTO processMoveEnd moveToObstacle:    curX = x    curY = y     DO WHILE getAdjacentCell(curX, curY, dir) = 0      SELECT CASE dir        CASE "l": curX = curX - 1        CASE "r": curX = curX + 1        CASE "u": curY = curY - 1        CASE "d": curY = curY + 1      END SELECT    LOOP  RETURN processBlock:     merge = 0    IF gGrid(x, y) <> 0 THEN ' have block       GOSUB moveToObstacle ' figure out where it can be moved to      IF getAdjacentCell(curX, curY, dir) = gGrid(x, y) AND wasMerge = 0 THEN  ' obstacle can be merged with        merge = 1        wasMerge = 1      ELSE        wasMerge = 0      END IF       IF curX <> x OR curY <> y OR merge = 1 THEN        mergeDirX = 0        mergeDirY = 0        IF merge = 1 THEN          SELECT CASE dir            CASE "l": mergeDirX = -1            CASE "r": mergeDirX = 1            CASE "u": mergeDirY = -1            CASE "d": mergeDirY = 1          END SELECT        END IF         CALL moveBlock(x, y, curX + mergeDirX, curY + mergeDirY, merge) ' move to before obstacle or merge        hasMoved = 1      END IF    END IF  RETURN processMoveEnd:   IF hasMoved = 1 THEN addblock  renderGrid  updateScore END SUB SUB renderGrid  FOR x = 0 TO gGridSize - 1    FOR y = 0 TO gGridSize - 1      CALL drawNumber(gGrid(x, y), x, y)    NEXT y  NEXT xEND SUB SUB updateScore  LOCATE 1, 10  PRINT "Score:" + STR\$(gScore)END SUB `

## 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, 0, sizeof(game));    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.

## Delphi

Thanks for Rudy Velthuis [1].
Translation of: C++
` program Game2048; {\$APPTYPE CONSOLE} uses  System.SysUtils,  System.Math,  Velthuis.Console; type  TTile = class    Value: integer;    IsBlocked: Boolean;    constructor Create;  end;   TMoveDirection = (mdUp, mdDown, mdLeft, mdRight);   TG2048 = class    FisDone, FisWon, FisMoved: boolean;    Fscore: Cardinal;    FBoard: array[0..3, 0..3] of TTile;    function GetLine(aType: byte): string;  public    constructor Create;    destructor Destroy; override;    procedure InitializeBoard();    procedure FinalizeBoard();    procedure Loop;    procedure DrawBoard();    procedure WaitKey();    procedure AddTile();    function CanMove(): boolean;    function TestAdd(x, y, value: Integer): boolean;    procedure MoveHorizontally(x, y, d: integer);    procedure MoveVertically(x, y, d: integer);    procedure Move(direction: TMoveDirection);  end;  { TTile } constructor TTile.Create;begin  Value := 0;  IsBlocked := false;end; { TG2048 } procedure TG2048.AddTile;var  y, x, a, b: Integer;  r: Double;begin  for y := 0 to 3 do  begin    for x := 0 to 3 do    begin      if Fboard[x, y].Value <> 0 then        continue;      repeat        a := random(4);        b := random(4);      until not (Fboard[a, b].Value <> 0);      r := Random;      if r > 0.89 then        Fboard[a, b].Value := 4      else        Fboard[a, b].Value := 2;      if CanMove() then      begin        Exit;      end;    end;  end;  FisDone := true;end; function TG2048.CanMove: boolean;var  y, x: Integer;begin  for y := 0 to 3 do  begin    for x := 0 to 3 do    begin      if Fboard[x, y].Value = 0 then      begin        Exit(true);      end;    end;  end;  for y := 0 to 3 do  begin    for x := 0 to 3 do    begin      if TestAdd(x + 1, y, Fboard[x, y].Value) or TestAdd(x - 1, y, Fboard[x, y].Value)        or TestAdd(x, y + 1, Fboard[x, y].Value) or TestAdd(x, y - 1, Fboard[x,        y].Value) then      begin        Exit(true);      end;    end;  end;  Exit(false);end; constructor TG2048.Create;begin  FisDone := false;  FisWon := false;  FisMoved := true;  Fscore := 0;  InitializeBoard();  Randomize;end; destructor TG2048.Destroy;begin  FinalizeBoard;  inherited;end; procedure TG2048.DrawBoard;var  y, x: Integer;  color: byte;  lineFragment, line: string;begin  ClrScr;  HighVideo;  writeln('Score: ', Fscore: 3, #10);  TextBackground(White);  TextColor(black);  for y := 0 to 3 do  begin    if y = 0 then      writeln(GetLine(0))    else      writeln(GetLine(1));     Write('  '#\$2551' ');    for x := 0 to 3 do    begin      if Fboard[x, y].Value = 0 then      begin        Write('    ');      end      else      begin        color := Round(Log2(Fboard[x, y].Value));        TextColor(14 - color);        Write(Fboard[x, y].Value: 4);        TextColor(Black);      end;      Write(' '#\$2551' ');    end;    writeln(' ');  end;  writeln(GetLine(2), #10#10);  TextBackground(Black);  TextColor(White);end; procedure TG2048.FinalizeBoard;var  y, x: integer;begin  for y := 0 to 3 do    for x := 0 to 3 do      FBoard[x, y].Free;end; function TG2048.GetLine(aType: byte): string;var  fragment, line: string;  bgChar, edChar, mdChar: char;begin   case aType of    0:      begin        bgChar := #\$2554;        edChar := #\$2557;        mdChar := #\$2566;      end;    1:      begin        bgChar := #\$2560;        edChar := #\$2563;        mdChar := #\$256C;      end;     2:      begin        bgChar := #\$255A;        edChar := #\$255D;        mdChar := #\$2569;      end;  end;  fragment := string.create(#\$2550, 6);  line := fragment + mdChar + fragment + mdChar + fragment + mdChar + fragment;  Result := '  '+bgChar + line + edChar + '  ';end; procedure TG2048.InitializeBoard;var  y, x: integer;begin  for y := 0 to 3 do    for x := 0 to 3 do      FBoard[x, y] := TTile.Create;end; procedure TG2048.Loop;begin  AddTile();  while (true) do  begin    if (FisMoved) then      AddTile();     DrawBoard();    if (FisDone) then      break;     WaitKey();  end;   if FisWon then    Writeln('You''ve made it!')  else    Writeln('Game Over!');end; procedure TG2048.Move(direction: TMoveDirection);var  x, y: Integer;begin  case direction of    mdUp:      begin        for x := 0 to 3 do        begin          y := 1;          while y < 4 do          begin            if Fboard[x, y].Value <> 0 then              MoveVertically(x, y, -1);            Inc(y);          end;        end;      end;    mdDown:      begin        for x := 0 to 3 do        begin          y := 2;          while y >= 0 do          begin            if Fboard[x, y].Value <> 0 then              MoveVertically(x, y, 1);            Dec(y);          end;        end;      end;    mdLeft:      begin        for y := 0 to 3 do        begin          x := 1;          while x < 4 do          begin            if Fboard[x, y].Value <> 0 then              MoveHorizontally(x, y, -1);            Inc(x);          end;        end;      end;    mdRight:      begin        for y := 0 to 3 do        begin          x := 2;          while x >= 0 do          begin            if Fboard[x, y].Value <> 0 then              MoveHorizontally(x, y, 1);            Dec(x);          end;        end;      end;  end;end; procedure TG2048.MoveHorizontally(x, y, d: integer);begin  if (FBoard[x + d, y].Value <> 0) and (FBoard[x + d, y].Value = FBoard[x, y].Value)    and (not FBoard[x + d, y].IsBlocked) and (not FBoard[x, y].IsBlocked) then  begin    FBoard[x, y].Value := 0;    FBoard[x + d, y].Value := FBoard[x + d, y].Value * 2;    Fscore := Fscore + (FBoard[x + d, y].Value);    FBoard[x + d, y].IsBlocked := true;    FisMoved := true;  end  else if ((FBoard[x + d, y].Value = 0) and (FBoard[x, y].Value <> 0)) then  begin    FBoard[x + d, y].Value := FBoard[x, y].Value;    FBoard[x, y].Value := 0;    FisMoved := true;  end;  if d > 0 then  begin    if x + d < 3 then    begin      MoveHorizontally(x + d, y, 1);    end;  end  else  begin    if x + d > 0 then    begin      MoveHorizontally(x + d, y, -1);    end;  end;end; procedure TG2048.MoveVertically(x, y, d: integer);begin  if (Fboard[x, y + d].Value <> 0) and (Fboard[x, y + d].Value = Fboard[x, y].Value)    and (not Fboard[x, y].IsBlocked) and (not Fboard[x, y + d].IsBlocked) then  begin    Fboard[x, y].Value := 0;    Fboard[x, y + d].Value := Fboard[x, y + d].Value * 2;    Fscore := Fscore + (Fboard[x, y + d].Value);    Fboard[x, y + d].IsBlocked := true;    FisMoved := true;  end  else if ((Fboard[x, y + d].Value = 0) and (Fboard[x, y].Value <> 0)) then  begin    Fboard[x, y + d].Value := Fboard[x, y].Value;    Fboard[x, y].Value := 0;    FisMoved := true;  end;  if d > 0 then  begin    if y + d < 3 then    begin      MoveVertically(x, y + d, 1);    end;  end  else  begin    if y + d > 0 then    begin      MoveVertically(x, y + d, -1);    end;  end;end; function TG2048.TestAdd(x, y, value: Integer): boolean;begin  if (x < 0) or (x > 3) or (y < 0) or (y > 3) then    Exit(false);   Exit(Fboard[x, y].value = value);end; procedure TG2048.WaitKey;var  y, x: Integer;begin  FisMoved := false;  writeln('(W) Up (S) Down (A) Left (D) Right (ESC)Exit');  case ReadKey of    'W', 'w':      Move(TMoveDirection.mdUp);    'A', 'a':      Move(TMoveDirection.mdLeft);    'S', 's':      Move(TMoveDirection.mdDown);    'D', 'd':      Move(TMoveDirection.mdRight);    #27:      FisDone := true;  end;   for y := 0 to 3 do    for x := 0 to 3 do      Fboard[x, y].IsBlocked := false;end; var  Game: TG2048;begin  with TG2048.Create do  begin    Loop;    Free;  end;  Writeln('Press Enter to exit');  Readln;end.`
Output:
```Score: 6472

╔══════╦══════╦══════╦══════╗
║    2 ║    8 ║    2 ║    4 ║
╠══════╬══════╬══════╬══════╣
║   16 ║   64 ║    8 ║   64 ║
╠══════╬══════╬══════╬══════╣
║    8 ║  512 ║  256 ║    2 ║
╠══════╬══════╬══════╬══════╣
║    2 ║    4 ║   16 ║    4 ║
╚══════╩══════╩══════╩══════╝

Game Over!
Press Enter to exit
```

## 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 [2]

`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        ] `

## F#

The following code can be executed as is using F# Interactive from system command line ("fsi.exe 2048.fsx") but not from Visual Studio F# Interactive window due to the way it access keyboard (the System.Console.ReadKey() function).

` // the board is represented with a list of 16 integerslet empty = List.init 16 (fun _ -> 0)let win = List.contains 2048  // a single movement (a hit) consists of stacking and then possible joininglet rec stack = function    | 0 :: t -> stack t @ [0]    | h :: t -> h :: stack t    | [] -> []let rec join = function    | a :: b :: c when a = b -> (a + b :: join c) @ [0]    | a :: b -> a :: join b    | [] -> []let hit = stack >> joinlet hitBack = List.rev >> hit >> List.revlet rows = List.chunkBySize 4let left = rows >> List.map hit >> List.concatlet right = rows >> List.map hitBack >> List.concatlet up = rows >> List.transpose >> List.map hit >> List.transpose >> List.concatlet down = rows >> List.transpose >> List.map hitBack >> List.transpose >> List.concat let lose g = left g = g && right g = g && up g = g && down g = g // spawn a 2 or occasionally a 4 at a random unoccupied positionlet random = System.Random()let spawnOn g =    let newTileValue = if random.Next 10 = 0 then 4 else 2    let numZeroes = List.filter ((=) 0) >> List.length    let newPosition = g |> numZeroes |> random.Next    let rec insert what where = function        | 0 :: tail when numZeroes tail = where -> what :: tail        | h :: t -> h :: insert what where t        | [] -> []    insert newTileValue newPosition g let show =    let line = List.map (sprintf "%4i") >> String.concat " "    rows >> List.map line >> String.concat "\n" >> printf "\n%s\n" // use an empty list as a sign of user interruptlet quit _ = []let quitted = List.isEmpty let dispatch = function | 'i' -> up | 'j' -> left | 'k' -> down | 'l' -> right | 'q' -> quit | _ -> idlet key() = System.Console.ReadKey().KeyChar |> charlet turn state =    show state    let nextState = (key() |> dispatch) state    if nextState <> state && not (quitted nextState) then spawnOn nextState else nextState let play() =    let mutable state = spawnOn empty    while not (win state || lose state || quitted state) do state <- turn state    if quitted state then printfn "User interrupt" else        show state        if win state then printf "You win!"        if lose state then printf "You lose!" play() `

## 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:
```

## FreeBASIC

Based On MichD's original code (https://github.com/michd/2048-qbasic)

`#define EXTCHAR Chr(255) '--- Declaration of global variables ---Dim Shared As Integer gGridSize = 4  'grid size (4 -> 4x4)Dim Shared As Integer gGrid(gGridSize, gGridSize)Dim Shared As Integer gScoreDim Shared As Integer curX, curYDim Shared As Integer hasMoved, wasMerge  ' Don't touch these numbers, seriouslyDim Shared As Integer gOriginX, gOriginYgOriginX = 75 'pixel X of top left of gridgOriginY = 12 'pixel Y of top right of gridaDim Shared As Integer gTextOriginX, gTextOriginY, gSquareSidegTextOriginX = 11gTextOriginY = 3gSquareSide = 38 'width/height of block in pixels 'set up all the things!Dim Shared As Integer gDebug = 0 '--- SUBroutines and FUNCtions ---Sub addblock    Dim As Integer emptyCells(gGridSize * gGridSize, 2)    Dim As Integer emptyCellCount = 0    Dim As Integer x, y, index, num     For x = 0 To gGridSize - 1        For y = 0 To gGridSize - 1            If gGrid(x, y) = 0 Then                emptyCells(emptyCellCount, 0) = x                emptyCells(emptyCellCount, 1) = y                emptyCellCount += 1            End If        Next y    Next x     If emptyCellCount > 0 Then        index = Int(Rnd * emptyCellCount)        num = Cint(Rnd + 1) * 2        gGrid(emptyCells(index, 0), emptyCells(index, 1)) = num    End IfEnd Sub Function pad(num As Integer) As String    Dim As String strNum = Ltrim(Str(num))     Select Case Len(strNum)    Case 1: Return "  " + strNum + " "    Case 2: Return " " + strNum + " "    Case 3: Return " " + strNum    Case 4: Return strNum    End SelectEnd Function Sub drawNumber(num As Integer, xPos As Integer, yPos As Integer)    Dim As Integer c, x, y    Select Case num    Case 0:    c = 16    Case 2:    c = 2    Case 4:    c = 3    Case 8:    c = 4    Case 16:   c = 5    Case 32:   c = 6    Case 64:   c = 7    Case 128:  c = 8    Case 256:  c = 9    Case 512:  c = 10    Case 1024: c = 11    Case 2048: c = 12    Case 4096: c = 13    Case 8192: c = 13    Case Else: c = 13    End Select     x = xPos *(gSquareSide + 2) + gOriginX + 1    y = yPos *(gSquareSide + 2) + gOriginY + 1    Line(x + 1, y + 1)-(x + gSquareSide - 1, y + gSquareSide - 1), c, BF     If num > 0 Then        Locate gTextOriginY + 1 +(yPos * 5), gTextOriginX +(xPos * 5) : Print "    "        Locate gTextOriginY + 2 +(yPos * 5), gTextOriginX +(xPos * 5) : Print pad(num)        Locate gTextOriginY + 3 +(yPos * 5), gTextOriginX +(xPos * 5)    End IfEnd Sub Function getAdjacentCell(x As Integer, y As Integer, d As String) As Integer    If (d = "l" And x = 0) Or (d = "r" And x = gGridSize - 1) Or (d = "u" And y = 0) Or (d = "d" And y = gGridSize - 1) Then        getAdjacentCell = -1    Else        Select Case d        Case "l": getAdjacentCell = gGrid(x - 1, y)        Case "r": getAdjacentCell = gGrid(x + 1, y)        Case "u": getAdjacentCell = gGrid(x, y - 1)        Case "d": getAdjacentCell = gGrid(x, y + 1)        End Select    End IfEnd Function 'Draws the outside grid(doesn't render tiles)Sub initGraphicGrid    Dim As Integer x, y, gridSide =(gSquareSide + 2) * gGridSize     Line(gOriginX, gOriginY)-(gOriginX + gridSide, gOriginY + gridSide), 14, BF 'outer square, 3 thick    Line(gOriginX, gOriginY)-(gOriginX + gridSide, gOriginY + gridSide), 1, B 'outer square, 3 thick    Line(gOriginX - 1, gOriginY - 1)-(gOriginX + gridSide + 1, gOriginY + gridSide + 1), 1, B    Line(gOriginX - 2, gOriginY - 2)-(gOriginX + gridSide + 2, gOriginY + gridSide + 2), 1, B     For x = gOriginX + gSquareSide + 2 To gOriginX +(gSquareSide + 2) * gGridSize Step gSquareSide + 2  ' horizontal lines        Line(x, gOriginY)-(x, gOriginY + gridSide), 1    Next x     For y = gOriginY + gSquareSide + 2 To gOriginY +(gSquareSide + 2) * gGridSize Step gSquareSide + 2 ' vertical lines        Line(gOriginX, y)-(gOriginX + gridSide, y), 1    Next yEnd Sub 'Init the(data) grid with 0sSub initGrid    Dim As Integer x, y    For x = 0 To 3        For y = 0 To 3            gGrid(x, y) = 0        Next y    Next x     addblock    addblockEnd Sub Sub moveBlock(sourceX As Integer, sourceY As Integer, targetX As Integer, targetY As Integer, merge As Integer)    If sourceX < 0 Or sourceX >= gGridSize Or sourceY < 0 Or sourceY >= gGridSize And gDebug = 1 Then        Locate 0, 0 : Print "moveBlock: source coords out of bounds"    End If     If targetX < 0 Or targetX >= gGridSize Or targetY < 0 Or targetY >= gGridSize And gDebug = 1 Then        Locate 0, 0 : Print "moveBlock: source coords out of bounds"    End If     Dim As Integer sourceSquareValue = gGrid(sourceX, sourceY)    Dim As Integer targetSquareValue = gGrid(targetX, targetY)     If merge = 1 Then        If sourceSquareValue = targetSquareValue Then            gGrid(sourceX, sourceY) = 0            gGrid(targetX, targetY) = targetSquareValue * 2            gScore += targetSquareValue * 2 ' Points!        Elseif gDebug = 1 Then            Locate 0, 0 : Print "moveBlock: Attempted to merge unequal sqs"        End If    Else        If targetSquareValue = 0 Then            gGrid(sourceX, sourceY) = 0            gGrid(targetX, targetY) = sourceSquareValue        Elseif gDebug = 1 Then            Locate 0, 0 : Print "moveBlock: Attempted to move to non-empty block"        End If    End IfEnd Sub Function pColor(r As Integer, g As Integer, b As Integer) As Integer    Return (r + g * 256 + b * 65536)End Function Sub moveToObstacle(x As Integer, y As Integer, direcc As String)    curX = x : curY = y     Do While getAdjacentCell(curX, curY, direcc) = 0        Select Case direcc        Case "l": curX -= 1        Case "r": curX += 1        Case "u": curY -= 1        Case "d": curY += 1        End Select    LoopEnd Sub Sub processBlock(x As Integer, y As Integer, direcc As String)    Dim As Integer merge = 0, mergeDirX, mergeDirY    If gGrid(x, y) <> 0 Then ' have block        moveToObstacle(x, y, direcc) ' figure out where it can be moved to        If getAdjacentCell(curX, curY, direcc) = gGrid(x, y) And wasMerge = 0 Then  ' obstacle can be merged with            merge = 1            wasMerge = 1        Else            wasMerge = 0        End If         If curX <> x Or curY <> y Or merge = 1 Then            mergeDirX = 0            mergeDirY = 0            If merge = 1 Then                Select Case direcc                Case "l": mergeDirX = -1                Case "r": mergeDirX = 1                Case "u": mergeDirY = -1                Case "d": mergeDirY = 1                End Select            End If             moveBlock(x, y, curX + mergeDirX, curY + mergeDirY, merge) ' move to before obstacle or merge            hasMoved = 1        End If    End IfEnd Sub Sub renderGrid    Dim As Integer x, y    For x = 0 To gGridSize - 1        For y = 0 To gGridSize - 1            drawNumber(gGrid(x, y), x, y)        Next y    Next xEnd Sub Sub updateScore    Locate 1, 10 : Print Using "Score: #####"; gScoreEnd Sub Sub processMove(direcc As String) '' direcc can be 'l', 'r', 'u', or 'd'    Dim As Integer x, y    hasMoved = 0     If direcc = "l" Then        For y = 0 To gGridSize - 1            wasMerge = 0            For x = 0 To gGridSize - 1                processBlock(x,y,direcc)            Next x        Next y    Elseif direcc = "r" Then        For y = 0 To gGridSize - 1            wasMerge = 0            For x = gGridSize - 1 To 0 Step -1                processBlock(x,y,direcc)            Next x        Next y    Elseif direcc = "u" Then        For x = 0 To gGridSize - 1            wasMerge = 0            For y = 0 To gGridSize - 1                processBlock(x,y,direcc)            Next y        Next x    Elseif direcc = "d" Then        For x = 0 To gGridSize - 1            wasMerge = 0            For y = gGridSize - 1 To 0 Step -1                processBlock(x,y,direcc)            Next y        Next x    End If     If hasMoved = 1 Then addblock    renderGrid    updateScoreEnd Sub  '--- Main Program ---Screen 8Windowtitle "2048"Palette 1, pColor(35, 33, 31)Palette 2, pColor(46, 46, 51)Palette 3, pColor(59, 56, 50)Palette 4, pColor(61, 44, 30)Palette 5, pColor(61, 37, 25)Palette 6, pColor(62, 31, 24)Palette 7, pColor(62, 24, 15)Palette 8, pColor(59, 52, 29)Palette 9, pColor(59, 51, 24)Palette 10, pColor(59, 50, 20)Palette 11, pColor(59, 49, 16)Palette 12, pColor(59, 49, 12)Palette 13, pColor(15, 15, 13)Palette 14, pColor(23, 22, 20) Randomize TimerClsDo    initGrid    initGraphicGrid    renderGrid    updateScore     gScore = 0     Locate 23, 10 : Print "Move with arrow keys."    Locate 24, 12 : Print "(R)estart, (Q)uit"     Dim As String k    Do        Do            k = Inkey        Loop Until k <> ""         Select Case k        Case EXTCHAR + Chr(72) 'up            processMove("u")        Case EXTCHAR + Chr(80) 'down            processMove("d")        Case EXTCHAR + Chr(77) 'right            processMove("r")        Case EXTCHAR + Chr(75) 'left            processMove("l")        Case "q", "Q", Chr(27) 'escape            End        Case "r", "R"            Exit Do        End Select    LoopLoop`

## 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")    set_gtk_property!(newgame, :label, "New Game")    set_gtk_property!(newgame, :is_important, true)    undomove = ToolButton("Undo Move")    set_gtk_property!(undomove, :label, "Undo Move")    set_gtk_property!(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}(undef, bsize, bsize)    for i in 1:bsize, j in 1:bsize        grid[i,j] = buttons[i,j] = Button()        set_gtk_property!(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] : " "            set_gtk_property!(buttons[i,j], :label, label)        end        set_gtk_property!(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            set_gtk_property!(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")    Gtk.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?
```

## Latitude

Takes input on stdin using the words "left", "right", "up", "down".

` use 'format import '[format].use 'random. Pos := Object clone tap {   self x := 0.  self y := 0.   self move := {    localize.    case (\$1) do {      when 'left  do { pos (this x - 1, this y). }.      when 'right do { pos (this x + 1, this y). }.      when 'down  do { pos (this x, this y + 1). }.      when 'up    do { pos (this x, this y - 1). }.    }.  }.   self == := {    (self x == \$1 x) and (self y == \$1 y).  }.   self inBounds? := {    (self x >= 0) and (self x < 4) and (self y >= 0) and (self y < 4).  }.   self toString := {    format "pos (~S, ~S)" call (self x, self y).  }. }. pos := {  takes '[x, y].  Pos clone tap {    self x := x.    self y := y.  }.}. allSquares := [] tap {  localize.  0 upto 4 visit {    takes '[y].    0 upto 4 visit {      takes '[x].      this pushBack (pos (x, y)).    }.  }.}. sample := {  \$1 nth (random nextInt mod (\$1 length)).}. Grid ::= Object clone tap {   self grid := 16 times to (Array) map { 0. }.   self clone := {    Parents above (parent self, 'clone) call tap {      self grid := self grid clone.    }.  }.   toIndex := {    \$1 x + 4 * \$1 y.  }.   self at := {    self grid nth (toIndex).  }.   self at= := {    self grid nth= (toIndex).  }.   self isBlank? := {    self at == 0.  }.   self spawnNew := {    localize.    candidates := allSquares filter { this isBlank?. } to (Array).    if (candidates empty?) then {      'gameover.    } else {      p := sample (candidates).      this at (p) = if (random nextInt mod 10 == 0) then 4 else 2.      'okay.    }.  }.   canMoveCell := {    takes '[grid, src, dir].    dest := src move (dir).    if (dest inBounds?) then {      vs := grid at (src).      vd := grid at (dest).      (vs /= 0) and ((vd == 0) or (vs == vd)).    } else {      False.    }.  }.   self canMove := {    localize.    takes '[dir].    allSquares any { canMoveCell (this, \$1, dir). }.  }.   self validMoves := {    '[left, right, up, down] filter { parent self canMove. } to (Array).  }.   ;; Calculates the order of iteration for performing the moves  axisCalc := {    takes '[dir, major, minor].    case (dir) do {      when 'left do { pos (major, minor). }.      when 'right do { pos (3 - major, minor). }.      when 'up do { pos (minor, major). }.      when 'down do { pos (minor, 3 - major). }.    }.  }.   moveFrom := {    takes '[grid, src, dir, locked].    dest := src move (dir).    local 'result = Nil.    dest inBounds? ifTrue {      locked contains (dest) ifFalse {        vs := grid at (src).        vd := grid at (dest).        cond {          when (vd == 0) do {            grid at (dest) = vs.            grid at (src) = 0.            result = moveFrom (grid, dest, dir, locked).          }.          when (vd == vs) do {            grid at (dest) = vs + vd.            grid at (src) = 0.            result = moveFrom (grid, dest, dir, locked).            ;; We merged, so lock the final result cell.            locked pushBack (result).          }.        }.      }.    }.    (result) or (src).  }.   self doMove := {    localize.    takes '[dir].    locked := [].    0 upto 4 do {      takes '[major].      0 upto 4 do {        takes '[minor].        src := axisCalc (dir, major, minor).        moveFrom: this, src, dir, locked.      }.    }.  }.   self pretty := {    localize.    lines := [].    row := "+----+----+----+----+".    lines pushBack (row).    0 upto 4 visit {      takes '[y].      local 'line = "|".      0 upto 4 visit {        takes '[x].        n := this at (pos (x, y)).        if (n == 0) then {          line = line ++ "    |".        } else {          line = line ++ n toString padRight (" ", 4) ++ "|".        }.      }.      lines pushBack (line).      lines pushBack (row).    }.    lines joinText "\n".  }.   self toArray := {    allSquares map { parent self at. }.  }. }. grid := Grid clone.endgame := loop* {  ;; Check for victory  (grid toArray any { \$1 >= 2048. }) ifTrue {    last 'victory.  }.  ;; Check for game over  result := grid spawnNew.  (result == 'gameover) ifTrue {    last 'gameover.  }.  valid := grid validMoves.  valid empty? ifTrue {    last 'gameover.  }.  \$stdout putln: grid pretty.  move := loop* {    \$stdout puts: "Your move (left, right, up, down)> ".    move := \$stdin readln intern.    valid contains (move) ifTrue { last (move). }.  }.  grid doMove (move).}.\$stdout putln: grid pretty. if (endgame == 'victory) then {  \$stdout putln: "You win!".} else {  \$stdout putln: "Better luck next time!".}. `

Sample output:

```+----+----+----+----+
|16  |4   |    |    |
+----+----+----+----+
|4   |    |    |    |
+----+----+----+----+
|    |    |    |    |
+----+----+----+----+
|    |2   |    |    |
+----+----+----+----+
Your move (left, right, up, down)>```

## Lua

Sadly, ANSI C doesn't have low-level keyboard input, so neither does vanilla Lua, so the input is a bit cumbersome (wasd PLUS enter).

`-- 2048 for Lua 5.1-5.4, 12/3/2020 dblocal unpack = unpack or table.unpack -- for 5.3 +/- compatibilitygame = {  cell = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},  best = 0,  draw = function(self)    local t = self.cell    print("+----+----+----+----+")    for r=0,12,4 do      print(string.format("|%4d|%4d|%4d|%4d|\n+----+----+----+----+",t[r+1],t[r+2],t[r+3],t[r+4]))    end  end,  incr = function(self)    local t,open = self.cell,{}    for i=1,16 do if t[i]==0 then open[#open+1]=i end end    t[open[math.random(#open)]] = math.random()<0.1 and 4 or 2  end,  pack = function(self,ofr,oto,ost,ifr,ito,ist)    local t = self.cell    for outer=ofr,oto,ost do      local skip = 0      for inner=ifr,ito,ist do        local i = outer+inner        if t[i]==0 then skip=skip+1 else if skip>0 then t[i-skip*ist],t[i],self.diff = t[i],0,true end end      end    end  end,  comb = function(self,ofr,oto,ost,ifr,ito,ist)    local t = self.cell    for outer=ofr,oto,ost do      for inner=ifr,ito-ist,ist do        local i,j = outer+inner,outer+inner+ist        if t[i]>0 and t[i]==t[j] then t[i],t[j],self.diff,self.best = t[i]*2,0,true,math.max(self.best,t[i]*2) end      end    end  end,  move = function(self,dir)    local loopdata = {{0,12,4,1,4,1},{0,12,4,4,1,-1},{1,4,1,0,12,4},{1,4,1,12,0,-4}}    local ofr,oto,ost,ifr,ito,ist = unpack(loopdata[dir])    self:pack(ofr,oto,ost,ifr,ito,ist)    self:comb(ofr,oto,ost,ifr,ito,ist)    self:pack(ofr,oto,ost,ifr,ito,ist)  end,  full = function(self)    local t = self.cell    for r=0,12,4 do      for c=1,4 do        local i,v = r+c,t[r+c]        if (v==`