Task

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



Rules of the game

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


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



Requirements

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

 to the right should result in: 
               ......[4][4] 

 and not:
               .........[8] 

   "Move direction priority".   
  If more than one variant of combining is possible,   move direction shall indicate which combination will take effect. 
  For example, moving the tile row of:
               ...[2][2][2] 

 to the right should result in:
               ......[2][4] 

 and not:
               ......[4][2] 



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


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