#lose the game
#! /usr/bin/env python
import sys,os,subprocess
from stat import *
#!/usr/bin/env bash
#important variables
declare -ia board    # array that keeps track of game status
#important variables
_rc = subprocess.call(["declare","-ia","board"])
declare -i pieces    # number of pieces present on board
# array that keeps track of game status
_rc = subprocess.call(["declare","-i","pieces"])
declare -i score=0   # score variable
# number of pieces present on board
_rc = subprocess.call(["declare","-i",score=0])
declare -i flag_skip # flag that prevents doing more than one operation on
# score variable
_rc = subprocess.call(["declare","-i","flag_skip"])
                     # single field in one step
declare -i moves     # stores number of possible moves to determine if player lost 
# flag that prevents doing more than one operation on
# single field in one step
_rc = subprocess.call(["declare","-i","moves"])
                     # the game
declare ESC=$'\e'    # escape byte
# stores number of possible moves to determine if player lost 
# the game
_rc = subprocess.call(["declare",ESC=r''])
declare header="Bash 2048 v1.1 (https://github.com/mydzor/bash2048)"
# escape byte
_rc = subprocess.call(["declare",header="Bash 2048 v1.1 (https://github.com/mydzor/bash2048)"])
#default config
declare -i board_size=4
#default config
_rc = subprocess.call(["declare","-i",board_size=4])
declare -i target=2048
_rc = subprocess.call(["declare","-i",target=2048])
#for colorizing numbers
declare -a colors
#for colorizing numbers
_rc = subprocess.call(["declare",and,"colors"])
colors[2]=33         # yellow text
colors[2]=33
colors[4]=32         # green text
# yellow text
colors[4]=32
colors[8]=34         # blue text
# green text
colors[8]=34
colors[16]=36        # cyan text
# blue text
colors[16]=36
colors[32]=35        # purple text
# cyan text
colors[32]=35
colors[64]="33m\033[7"        # yellow background
# purple text
colors[64]="33m\033[7"
colors[128]="32m\033[7"       # green background
# yellow background
colors[128]="32m\033[7"
colors[256]="34m\033[7"       # blue background
# green background
colors[256]="34m\033[7"
colors[512]="36m\033[7"       # cyan background
# blue background
colors[512]="36m\033[7"
colors[1024]="35m\033[7"      # purple background
# cyan background
colors[1024]="35m\033[7"
colors[2048]="31m\033[7"      # red background (won with default target)
# purple background
colors[2048]="31m\033[7"
exec 3>/dev/null     # no logging by default
# red background (won with default target)
_rc = subprocess.Popen("exec",shell=True,stdout=file('/dev/null','wb'))
trap "end_game 0" INT #handle INT signal
# no logging by default
_rc = subprocess.call(["trap","end_game 0","INT"])
#simplified replacement of seq command
function _seq {
  local cur=1
  local max
  local inc=1
  case $# in
    1) let max=$1;;
    2) let cur=$1
       let max=$2;;
    3) let cur=$1
       let inc=$2
       let max=$3;;
  esac
  while test $max -ge $cur; do
    printf "$cur "
    let cur+=inc
  done
}
#handle INT signal
#simplified replacement of seq command
def _seq () :

    cur=1
    "max"
    inc=1
    
    if ( str(len(sys.argv)) == '1'):
        max=str(sys.argv[1])
    elif ( str(len(sys.argv)) == '2'):
        cur=str(sys.argv[1])
        max=str(sys.argv[2])
    elif ( str(len(sys.argv)) == '3'):
        cur=str(sys.argv[1])
        inc=str(sys.argv[2])
        max=str(sys.argv[3])
    while (_rc = subprocess.call(["test",str(max),>=,str(cur)])):
        print( str(cur)+" " )
        
        "cur+=inc"
# print currect status of the game, last added pieces are marked red
function print_board {
  clear
  printf "$header pieces=$pieces target=$target score=$score\n"
  printf "Board status:\n" >&3
  printf "\n"
  printf '/------'
  for l in $(_seq 1 $index_max); do
    printf '|------'
  done
  printf '\\\n'
  for l in $(_seq 0 $index_max); do
    printf '|'
    for m in $(_seq 0 $index_max); do
      if let ${board[l*$board_size+m]}; then
        if let '(last_added==(l*board_size+m))|(first_round==(l*board_size+m))'; then
          printf '\033[1m\033[31m %4d \033[0m|' ${board[l*$board_size+m]}
        else
          printf "\033[1m\033[${colors[${board[l*$board_size+m]}]}m %4d\033[0m |" ${board[l*$board_size+m]}
        fi
        printf " %4d |" ${board[l*$board_size+m]} >&3
      else
        printf '      |'
        printf '      |' >&3
      fi
    done
    let l==$index_max || {
      printf '\n|------'
      for l in $(_seq 1 $index_max); do
        printf '|------'
      done
      printf '|\n'
      printf '\n' >&3
    }
  done
  printf '\n\\------'
  for l in $(_seq 1 $index_max); do
    printf '|------'
  done
  printf '/\n'
}
# print currect status of the game, last added pieces are marked red
def print_board () :
    global header
    global pieces
    global target
    global score
    global index_max
    global board
    global colors
    global l

    _rc = subprocess.call(["clear"])
    print( str(header)+" pieces="+str(pieces)+" target="+str(target)+" score="+str(score)+"\n" )
    
    print( "Board status:\n" )
    
    print( "\n" )
    
    print( r'/------' )
    
    for l in [os.popen("_seq 1 "+index_max).read()]:
        print( r'|------' )
    
    print( r'\\\n' )
    
    for l in [os.popen("_seq 0 "+index_max).read()]:
        print( r'|' )
        
        for m in [os.popen("_seq 0 "+index_max).read()]:
            if (str(board[l*$board_size+m]) ):
                if (r'(last_added==(l*board_size+m))|(first_round==(l*board_size+m))' ):
                    print( r'\033[1m\033[31m %4d \033[0m|' % (str(board[l*$board_size+m])) )
                
                else:
                    print( "\033[1m\033["+str(colors[${board[l*$board_size+m]}])+"m %4d\033[0m |" % (str(board[l*$board_size+m])) )
                
                print( " %4d |" % (str(board[l*$board_size+m])) )
            
            else:
                print( r'      |' )
                
                print( r'      |' )
        
        l="="+str(index_max) or { 
            print( r'\n|------' )
            
            for l in [os.popen("_seq 1 "+index_max).read()]:
                print( r'|------' )
            
            print( r'|\n' )
            
            print( r'\n' )
            
        }
    print( r'\n\\------' )
    
    for l in [os.popen("_seq 1 "+index_max).read()]:
        print( r'|------' )
    
    print( r'/\n' )
    
# Generate new piece on the board
# inputs:
#         $board  - original state of the game board
#         $pieces - original number of pieces
# outputs:
#         $board  - new state of the game board
#         $pieces - new number of pieces
function generate_piece {
  while true; do
    let pos=RANDOM%fields_total
    let board[$pos] || {
      let value=RANDOM%10?2:4
      board[$pos]=$value
      last_added=$pos
      printf "Generated new piece with value $value at position [$pos]\n" >&3
      break;
    }
  done
  let pieces++
}
# Generate new piece on the board
# inputs:
#         $board  - original state of the game board
#         $pieces - original number of pieces
# outputs:
#         $board  - new state of the game board
#         $pieces - new number of pieces
def generate_piece () :
    global pos
    global value
    global last_added

    while (True):
        pos="RANDOM%fields_total"
        "board["+str(pos)+"]" or { 
            value="RANDOM%10?2:4"
            "board["+str(pos)+"]="+str(value)
            last_added=str(pos)
            print( "Generated new piece with value "+str(value)+" at position ["+str(pos)+"]\n" )
            
            break
        }
    "pieces++"
# perform push operation between two pieces
# inputs:
#         $1 - push position, for horizontal push this is row, for vertical column
#         $2 - recipient piece, this will hold result if moving or joining
#         $3 - originator piece, after moving or joining this will be left empty
#         $4 - direction of push, can be either "up", "down", "left" or "right"
#         $5 - if anything is passed, do not perform the push, only update number 
#              of valid moves
#         $board - original state of the game board
# outputs:
#         $change    - indicates if the board was changed this round
#         $flag_skip - indicates that recipient piece cannot be modified further
#         $board     - new state of the game board
function push_pieces {
  case $4 in
    "up")
      let "first=$2*$board_size+$1"
      let "second=($2+$3)*$board_size+$1"
      ;;
    "down")
      let "first=(index_max-$2)*$board_size+$1"
      let "second=(index_max-$2-$3)*$board_size+$1"
      ;;
    "left")
      let "first=$1*$board_size+$2"
      let "second=$1*$board_size+($2+$3)"
      ;;
    "right")
      let "first=$1*$board_size+(index_max-$2)"
      let "second=$1*$board_size+(index_max-$2-$3)"
      ;;
  esac
  let ${board[$first]} || { 
    let ${board[$second]} && {
      if test -z $5; then
        board[$first]=${board[$second]}
        let board[$second]=0
        let change=1
        printf "move piece with value ${board[$first]} from [$second] to [$first]\n" >&3
      else
        let moves++
      fi
      return
    }
    return
  }
  let ${board[$second]} && let flag_skip=1
  let "${board[$first]}==${board[second]}" && { 
    if test -z $5; then
      let board[$first]*=2
      let "board[$first]==$target" && end_game 1
      let board[$second]=0
      let pieces-=1
      let change=1
      let score+=${board[$first]}
      printf "joined piece from [$second] with [$first], new value=${board[$first]}\n" >&3
    else
      let moves++
    fi
  }
}
# perform push operation between two pieces
# inputs:
#         $1 - push position, for horizontal push this is row, for vertical column
#         $2 - recipient piece, this will hold result if moving or joining
#         $3 - originator piece, after moving or joining this will be left empty
#         $4 - direction of push, can be either "up", "down", "left" or "right"
#         $5 - if anything is passed, do not perform the push, only update number 
#              of valid moves
#         $board - original state of the game board
# outputs:
#         $change    - indicates if the board was changed this round
#         $flag_skip - indicates that recipient piece cannot be modified further
#         $board     - new state of the game board
def push_pieces () :
    global board_size
    global board
    global first
    global second
    global change
    global flag_skip
    global target

    
    if ( str(sys.argv[4]) == '"up"'):
        "first="+str(sys.argv[2])+"*"+str(board_size)+"+"+str(sys.argv[1])
        "second=("+str(sys.argv[2])+"+"+str(sys.argv[3])+")*"+str(board_size)+"+"+str(sys.argv[1])
    elif ( str(sys.argv[4]) == '"down"'):
        "first=(index_max-"+str(sys.argv[2])+")*"+str(board_size)+"+"+str(sys.argv[1])
        "second=(index_max-"+str(sys.argv[2])+"-"+str(sys.argv[3])+")*"+str(board_size)+"+"+str(sys.argv[1])
    elif ( str(sys.argv[4]) == '"left"'):
        "first="+str(sys.argv[1])+"*"+str(board_size)+"+"+str(sys.argv[2])
        "second="+str(sys.argv[1])+"*"+str(board_size)+"+("+str(sys.argv[2])+"+"+str(sys.argv[3])+")"
    elif ( str(sys.argv[4]) == '"right"'):
        "first="+str(sys.argv[1])+"*"+str(board_size)+"+(index_max-"+str(sys.argv[2])+")"
        "second="+str(sys.argv[1])+"*"+str(board_size)+"+(index_max-"+str(sys.argv[2])+"-"+str(sys.argv[3])+")"
    str(board[$first]) or { 
        str(board[$second]) and { 
            if (_rc = subprocess.call(["test", not ,str(sys.argv[5])]) ):
                "board["+str(first)+"]="+str(board[$second])
                "board["+str(second)+"]=0"
                change=1
                print( "move piece with value "+str(board[$first])+" from ["+str(second)+"] to ["+str(first)+"]\n" )
            
            else:
                "moves++"
            return
        }
        return
    }
    str(board[$second]) and flag_skip=1
    str(board[$first])+"=="+str(board[second]) and { 
        if (_rc = subprocess.call(["test", not ,str(sys.argv[5])]) ):
            "board["+str(first)+"]*=2"
            "board["+str(first)+"]=="+str(target) and _rc = subprocess.call(["end_game",1])
            "board["+str(second)+"]=0"
            "pieces-=1"
            change=1
            "score+="+str(board[$first])
            print( "joined piece from ["+str(second)+"] with ["+str(first)+"], new value="+str(board[$first])+"\n" )
        
        else:
            "moves++"
    }
function apply_push {
  printf "\n\ninput: $1 key\n" >&3
  for i in $(_seq 0 $index_max); do
    for j in $(_seq 0 $index_max); do
      flag_skip=0
      let increment_max=index_max-j
      for k in $(_seq 1 $increment_max); do
        let flag_skip && break
        push_pieces $i $j $k $1 $2
      done 
    done
  done
}
def apply_push () :
    global index_max
    global flag_skip
    global increment_max

    print( "\n\ninput: "+str(sys.argv[1])+" key\n" )
    
    for i in [os.popen("_seq 0 "+index_max).read()]:
        for j in [os.popen("_seq 0 "+index_max).read()]:
            flag_skip=0
            increment_max="index_max-j"
            for k in [os.popen("_seq 1 "+increment_max).read()]:
                "flag_skip" and break
                push_pieces()
function check_moves {
  let moves=0
  apply_push up fake
  apply_push down fake
  apply_push left fake
  apply_push right fake
}
def check_moves () :
    global moves

    moves=0
    apply_push()
    apply_push()
    apply_push()
    apply_push()
function key_react {
  let change=0
  read -d '' -sn 1
  test "$REPLY" = "$ESC" && {
    read -d '' -sn 1 -t1
    test "$REPLY" = "[" && {
      read -d '' -sn 1 -t1
      case $REPLY in
        A) apply_push up;;
        B) apply_push down;;
        C) apply_push right;;
        D) apply_push left;;
      esac
    }
  } || {
    case $REPLY in
      k) apply_push up;;
      j) apply_push down;;
      l) apply_push right;;
      h) apply_push left;;
    esac
  }
}
def key_react () :
    global change
    global REPLY
    global ESC

    change=0
    -d = raw_input()
    _rc = subprocess.call(["test",str(REPLY),==,str(ESC)]) and { 
        -d = raw_input()
        _rc = subprocess.call(["test",str(REPLY),==,"["]) and { 
            -d = raw_input()
            
            if ( str(REPLY) == 'A'):
                apply_push()
            elif ( str(REPLY) == 'B'):
                apply_push()
            elif ( str(REPLY) == 'C'):
                apply_push()
            elif ( str(REPLY) == 'D'):
                apply_push()
        }
    } or { 
        
        if ( str(REPLY) == 'k'):
            apply_push()
        elif ( str(REPLY) == 'j'):
            apply_push()
        elif ( str(REPLY) == 'l'):
            apply_push()
        elif ( str(REPLY) == 'h'):
            apply_push()
    }
function end_game {
  print_board
  printf "GAME OVER\n"
  printf "Your score: $score\n"
  stty echo
  let $1 && {
    printf "Congratulations you have achieved $target\n"
    exit 0
  }
  printf "You have lost, better luck next time.\033[0m\n"
  exit 0
}
def end_game () :
    global score
    global target

    print_board()
    print( "GAME OVER\n" )
    
    print( "Your score: "+str(score)+"\n" )
    
    _rc = subprocess.call(["stty",print])
    str(sys.argv[1]) and { 
        print( "Congratulations you have achieved "+str(target)+"\n" )
        
        exit(0)
    }
    print( "You have lost, better luck next time.\033[0m\n" )
    
    exit(0)
function help {
  cat <<END_HELP
Usage: $1 [-b INTEGER] [-t INTEGER] [-l FILE] [-h]

  -b			specify game board size (sizes 3-9 allowed)
  -t			specify target score to win (needs to be power of 2)
  -l			log debug info into specified file
  -h			this help

END_HELP
}
def help () :
    _rc = subprocess.Popen("cat",shell=True,stdin=subprocess.PIPE)
    _rc.communicate("""Usage: $1 [-b INTEGER] [-t INTEGER] [-l FILE] [-h]
    
      -b			specify game board size (sizes 3-9 allowed)
      -t			specify target score to win (needs to be power of 2)
      -l			log debug info into specified file
      -h			this help
    
    """)
#parse commandline options
while getopts "b:t:l:h" opt; do
  case $opt in
    b ) board_size="$OPTARG"
      let '(board_size>=3)&(board_size<=9)' || {
        printf "Invalid board size, please choose size between 3 and 9\n"
        exit -1 
      };;
    t ) target="$OPTARG"
      printf "obase=2;$target\n" | bc | grep -e '^1[^1]*$'
      let $? && {
        printf "Invalid target, has to be power of two\n"
        exit -1 
      };;
    h ) help $0
        exit 0;;
    l ) exec 3>$OPTARG;;
    \?) printf "Invalid option: -"$opt", try $0 -h\n" >&2
            exit 1;;
    : ) printf "Option -"$opt" requires an argument, try $0 -h\n" >&2
            exit 1;;
  esac
done
#parse commandline options
while (_rc = subprocess.call(["getopts","b:t:l:h","opt"])):
    
    if ( str(opt) == 'b'):
        board_size=str(OPTARG)
        r'(board_size>=3)&(board_size<=9)' or { print( "Invalid board size, please choose size between 3 and 9\n" )
        
        exit(-1) }
    elif ( str(opt) == 't'):
        target=str(OPTARG)
        print( "obase=2;"+str(target)+"\n" )
         | _rc = subprocess.call(["bc"]) | _rc = subprocess.call(["grep","-e",r'^1[^1]*"+str()+"'])
        str(_rc) and { print( "Invalid target, has to be power of two\n" )
        
        exit(-1) }
    elif ( str(opt) == 'h'):
        help()
        exit(0)
    elif ( str(opt) == 'l'):
        _rc = subprocess.Popen("exec",shell=True,stdout=file('$OPTARG','wb'))
    
    elif ( str(opt) == '\?'):
        print( "Invalid option: -"+str(opt)+"\", try "+str(__file__)+" -h\n\"" )
        
        exit(1)
    elif ( str(opt) == ':'):
        print( "Option -"+str(opt)+"\" requires an argument, try "+str(__file__)+" -h\n\"" )
        
        exit(1)
#init board
let fields_total=board_size*board_size
#init board
fields_total="board_size*board_size"
let index_max=board_size-1
index_max="board_size-1"
for i in $(_seq 0 $fields_total); do board[$i]="0"; done
for i in [os.popen("_seq 0 "+fields_total).read()]:
    "board["+str(i)+"]=\"0\""
let pieces=0
pieces=0
generate_piece
generate_piece()
first_round=$last_added
first_round=str(last_added)
generate_piece
generate_piece()
while true; do
  print_board
  key_react
  let change && generate_piece
  first_round=-1
  let pieces==fields_total && {
   check_moves
   let moves==0 && end_game 0 #lose the game
  }
done
while (True):
    print_board()
    key_react()
    "change" and generate_piece()
    first_round=-1
    pieces="=fields_total" and { check_moves()
    moves==0 and end_game() }
ÿ