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)
  FI
OD;

win: print board(board); print(("you win!", new line)) EXIT
lose: print(("you lose!", new line))
)


/* 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  0
term_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  0
sa_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  0
poll_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              */
/*********************************/
.data
szMessOK:           .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 4
iGraine:            .int 123456
/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
.align 4
sZoneConv:      .skip 24
sBuffer:        .skip BUFFERSIZE
iTbCase:        .skip 4 * SIZE * SIZE
iEnd:           .skip 4                        @ 0 loop  1 = end loop
iTouche:        .skip 4                        @ value key pressed
stOldtio:       .skip term_fin                 @ old terminal state
stCurtio:       .skip term_fin                 @ current terminal state
stSigAction:    .skip sa_fin                   @ area signal structure
stSigAction1:   .skip sa_fin
stPoll1:        .skip poll_fin                 @ area poll structure
stPoll2:        .skip poll_fin
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main
main:                                 @ entry of program

1:                                    @ begin game loop
    ldr r0,iAdrszClear1
    bl affichageMess
    bl razTable
2:
    bl addDigit
    cmp r0,#-1
    beq 5f                            @ end game
    bl displayGame
3:
    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 10f
4:                                    @ last addition = 2048
    ldr r0,iAdrszMessOK
    bl affichageMess
    b 10f
5:                                    @ 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 szCarriageReturn
iAdrszMessNotOK:          .int szMessNotOK
iAdrszMessOK:             .int szMessOK
iAdrszMessNewGame:        .int szMessNewGame
iAdrsZoneConv:            .int sZoneConv
iAdrszClear1:             .int szClear1
/******************************************************************/
/*     raz table cases                                                   */
/******************************************************************/
razTable:
    push {r0-r2,lr}                  @ save  registers
    ldr r1,iAdriTbCase
    mov r0,#0
    mov r2,#0
1:
    str r0,[r1,r2,lsl #2]
    add r2,r2,#1
    cmp r2,#SIZE * SIZE
    blt 1b
100:
    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 100f
1:
    cmp r0,#0x41                  @ high arrow
    bne 2f
    bl moveUp
    b 100f
2:
    cmp r0,#0x43                  @ right arrow
    bne 3f
    bl moveRight
    b 100f
3:
    cmp r0,#0x44                  @ left arrow
    bne 4f
    bl moveLeft
    b 100f
4:
    ldr r0,iAdriTouche
    ldrb r0,[r0]
    cmp r0,#'q'                   @ quit game
    bne 5f
    mov r0,#-1
    b 100f
5:
    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 indice
1:
    mov r6,#0                   @ counter empty case
    mov r7,#0                   @ first digit
    mov r10,#0                  @ last digit to add
    mov r3,#0                   @ column indice
2:
    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,#1
3:
    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 Ok
4:
    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 1b
100:
    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,#0
1:
    mov r6,#0
    mov r7,#0
    mov r10,#0
    mov r3,#SIZE-1
2:
    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,#1
3:
    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,#1
4:
    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,#0
1:
    mov r6,#0
    mov r7,#0
    mov r10,#0
    mov r2,#SIZE-1
2:
    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,#1
3:
    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,#1
4:
    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,#0
1:
    mov r6,#0
    mov r7,#0
    mov r10,#0
    mov r2,#0
2:
    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,#1
3:
    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,#1
4:
    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,#0
1:
    ldr r2,[r1,r3,lsl #2]
    cmp r2,#0
    bne 2f
    str r3,[fp,r4,lsl #2]
    add r4,r4,#1
2:
    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 100f
3:                         @ 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,#0
1:
    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 affichageMess
2:
    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 affichageMess
3:
    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 affichageMess
4:
    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 szLineH
iAdrszLineV:         .int szLineV
iAdrszLineVT:        .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 100f
1:
    cmp r0,#2
    bne 2f
    ldr r0,iAdrszMess2
    b 100f
2:
    cmp r0,#4
    bne 3f
    ldr r0,iAdrszMess4
    b 100f
3:
    cmp r0,#8
    bne 4f
    ldr r0,iAdrszMess8
    b 100f
4:
    cmp r0,#16
    bne 5f
    ldr r0,iAdrszMess16
    b 100f
5:
    cmp r0,#32
    bne 6f
    ldr r0,iAdrszMess32
    b 100f
6:
    cmp r0,#64
    bne 7f
    ldr r0,iAdrszMess64
    b 100f
7:
    cmp r0,#128
    bne 8f
    ldr r0,iAdrszMess128
    b 100f
8:
    cmp r0,#256
    bne 9f
    ldr r0,iAdrszMess256
    b 100f
9:
    cmp r0,#512
    bne 10f
    ldr r0,iAdrszMess512
    b 100f
10:
    cmp r0,#1024
    bne 11f
    ldr r0,iAdrszMess1024
    b 100f
11:
    cmp r0,#2048
    bne 12f
    ldr r0,iAdrszMess2048
    b 100f
12:
    ldr r1,iAdrszMessErreur                       @ error message
    bl   displayError
100:
    pop {r1,lr}
    bx lr                   @ return
iAdrszMess0:          .int szMess0
iAdrszMess2:          .int szMess2
iAdrszMess4:          .int szMess4
iAdrszMess8:          .int szMess8
iAdrszMess16:         .int szMess16
iAdrszMess32:         .int szMess32
iAdrszMess64:         .int szMess64
iAdrszMess128:        .int szMess128
iAdrszMess256:        .int szMess256
iAdrszMess512:        .int szMess512
iAdrszMess1024:       .int szMess1024
iAdrszMess2048:        .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 iGraine
iNbDep1:     .int 0x343FD
iNbDep2:     .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 100f
1:
    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,#-1
98:                                               @ 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 100f
99:
    cmp r5,#0                                     @ no error or control-c ?
    ldreq r2,iAdriTouche                          @ key address
    ldreqb r0,[r2,#2]                             @ return key byte
    movne r0,r5                                   @ or error
100:
    pop {r1-r7, lr}
    bx lr
iSIG_IGN:                 .int 1
iAdriEnd:                 .int iEnd
iAdrstPoll1:              .int stPoll1
iAdriTouche:              .int iTouche
iAdrstOldtio:             .int stOldtio
iAdrstCurtio:             .int stCurtio
iAdrstSigAction:          .int stSigAction
iAdrstSigAction1:         .int stSigAction1
iAdrszMessErreur :        .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"


Grid := [], s := 16, w := h := S * 4.5
Gui, font, s%s%
Gui, add, text, y1
loop, 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]:=2
ShowGrid()
return

;------------------------------
GuiClose:
ExitApp
return
;------------------------------
#IfWinActive, 2048
;------------------------------
up::
move := false
loop, 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, AddNew
return
;------------------------------
Down::
move := false
loop, 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, AddNew
return
;------------------------------
Left::
move := false
loop, 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, AddNew
return
;------------------------------
Right::
move := false
loop, 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, AddNew
return

;------------------------------
#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
}
;------------------------------


:: 2048 Game Task from RosettaCode
:: Batch File Implementation v2.0.1

@echo off
setlocal enabledelayedexpansion

rem initialization
:begin_game
set "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 board
call :addtile
call :addtile

rem main game loop
:main_loop
call :display
echo(
echo(Keys: WASD (Slide Movement), N (New game), P (Exit)

rem get keypress trick
set "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 keypress
if /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_game
if /i "!key!" equ "P" exit /b 0
if "%valid_key%" equ "0" goto main_loop

rem check if the board changed
if %changed% neq 0 call :addtile

rem check for win condition
if %won% equ 1 (
    set "msg=Nice one... You WON^!^!"
    goto gameover
)

rem check for lose condition
if %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_loop

rem add number to a random blank tile
:addtile
set "blank_count=0"   %== blank tile counter ==%
set "new_tile="   %== clearing ==%
rem create pseudo-array blank_tiles
for /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 :EOF
set /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 2
if %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 :EOF

rem display the board
:display
cls
echo(2048 Game in Batch
echo(
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 :EOF

rem the main slider of numbers in tiles
:slide
rem %%A and %%B are used here because sliding direction is variable
for /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 :EOF

rem game over xD
:gameover
call :display
echo(
echo(!msg!
echo(
echo(Press any key to exit . . .
pause>nul
exit /b 0


SCREEN 13
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)

DIM SHARED gDebug
DIM SHARED gOriginX
DIM SHARED gOriginY
DIM SHARED gTextOriginX
DIM SHARED gTextOriginY
DIM SHARED gSquareSide
DIM 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 grid
gOriginY = 12 'pixel Y of top right of grid
gTextOriginX = 11
gTextOriginY = 3
gSquareSide = 38 'width/height of block in pixels

'set up all the things!
gDebug = 0

RANDOMIZE TIMER
CLS

start:
initGrid
initGraphicGrid
renderGrid
updateScore

gScore = 0

LOCATE 23, 1
PRINT "Move with arrow keys. (R)estart, (Q)uit"

' keyboard input loop
DO
  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 SELECT
LOOP

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 0s
SUB 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 x
END SUB

SUB updateScore
  LOCATE 1, 10
  PRINT "Score:" + STR$(gScore)
END SUB


      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


#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;
}


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);
        }
    }
}


#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" );
}


(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)


(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, otherwise
return 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 to
the 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 to
the 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))


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;
}


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.