bash 版 2048(转)

原创地址为:https://github.com/mydzor/bash2048

#!/usr/bin/env bash

#important variables
declare -ia board    # array that keeps track of game status
declare -i pieces    # number of pieces present on board
declare -i flag_skip # flag that prevents doing more than one operation on
                     # single field in one step
declare -i moves     # stores number of possible moves to determine if player lost 
                     # the game
declare ESC=$'\e'    # escape byte
declare header="Bash 2048 v1.0 (bugs: https://github.com/mydzor/bash2048/issues)"

#default config
declare -i board_size=4
declare -i target=2048

#for colorizing numbers
declare -a colors
colors[2]=33         # yellow text
colors[4]=32         # green text
colors[8]=34         # blue text
colors[16]=36        # cyan text
colors[32]=35        # purple text
colors[64]=43        # yellow background
colors[128]=42       # green background
colors[256]=44       # blue background
colors[512]=46       # cyan background
colors[1024]=45      # purple background
colors[2048]=41      # red background (won with default target)

exec 3>/dev/null     # no logging by default

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

# print currect status of the game, last added pieces are marked red
function print_board {
  clear
  echo $header pieces=$pieces target=$target
  echo Board status: >&3
  echo
  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'
}

# 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
      echo Generated new piece with value $value at position [$pos] >&3
      break;
    }
  done
  let 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
        echo "move piece with value ${board[$first]} from [$second] to [$first]" >&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
      echo "joined piece from [$second] with [$first], new value=${board[$first]}" >&3
    else
      let moves++
    fi
  }
}

function apply_push {
  echo input: $1 key >&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
}

function check_moves {
  let moves=0
  apply_push up fake
  apply_push down fake
  apply_push left fake
  apply_push right fake
}

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

function end_game {
  print_board
  echo GAME OVER
  let $1 && {
    echo "Congratulations you have achieved $target"
    exit 0
  }
  echo "You have lost, better luck next time."
  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
}


#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)' || {
        echo "Invalid board size, please choose size between 3 and 9"
        exit -1 
      };;
    t ) target="$OPTARG"
      echo "obase=2;$target" | bc | grep -e '^1[^1]*$'
      let $? && {
        echo "Invalid target, has to be power of two"
        exit -1 
      };;
    h ) help $0
        exit 0;;
    l ) exec 3>$OPTARG;;
    \?) echo "Invalid option: -"$OPTARG", try $0 -h" >&2
            exit 1;;
    : ) echo "Option -"$OPTARG" requires an argument, try $0 -h" >&2
            exit 1;;
  esac
done

#init board
let fields_total=board_size*board_size
let index_max=board_size-1
for i in $(_seq 0 $fields_total); do board[$i]="0"; done
let pieces=0
generate_piece
first_round=$last_added
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

 

posted @ 2014-03-26 10:03  lian4187  阅读(219)  评论(0编辑  收藏  举报