一个演示汉诺塔的脚本

上一篇 / 下一篇  2008-04-11 21:44:01 / 个人分类:Shell

主要是练习用tput来设置终端的前景,背景色,以及定位光标
水平菜,代码很长,让各位见笑了

代码:
#!/bin/bash

##usage: sh $0 n
##
## ------------------->X
## |
## |
## |
## |   disk 1 -------->     ()
## |   disk 2 -------->    (())
## |   disk 3 -------->   ((()))
## |   disk 4 -------->  (((())))  
## |   ............     ..........
## V   disk n-1 -->   ((((((()))))))
## Y   disk n --> ___(((((((())))))))______   _________________________   ________________________
##     ground -->|_________________________| |_________________________| |________________________|
##                         A                             A                            A
##                         |                             |                            |
##                         |                             |                            |
##                     source,1                        aid,2                  destination,3

usage="sh $0 non-negative number"
disks=$1
colums=$((($(tput cols)-4)/3))

## 底座的 Y 坐标
groundat=$(($(tput lines)-2))

## 底座source(或1)处,最顶层盘的中心坐标
sourcex=$(($colums/2))
sourcey=$(($groundat-$disks))

## 底座aid(或2)处,最顶层盘的中心坐标
aidx=$(($colums+2+$sourcex))
aidy=$(($groundat-1))
aidfirst=0

## 底座destination(或3)处,最顶层盘的中心坐标
destinationx=$(($colums*2+4+$sourcex))
destinationy=$(($groundat-1))
destinationfirst=0

coordinate=''


initdraw()
{
    init_drawdisks
    init_drawground
}

init_drawground()
{
    index=1
    tput cup $groundat 0
   
    for i in 1 2 3
    do
 tput setab $((1+$i))
 while(($index<=$i*$colums+($i-1)*2))
 do
     echo -ne ' '
     index=$(($index+1))
 done
 tput sgr0
 echo -n '  '
 index=$(($index+2))
    done

    tput cup $(tput lines) 0
}

init_drawdisks()
{
    for((offset=0,n=$disks;n>0;n--,offset++))
    do
 drawdisk $(($colums/2)) $(($groundat-1-$offset)) $((2+($n-1)*2)) $n
    done
}

drawdisk()
## 画一个中心在 ($1,$2),长度为 $3 的盘子
## 盘子的序号 $4 决定盘子的颜色
{
    disk=''
    for((tmp=$3/2;tmp>0;tmp--)); do disk=${disk}')'; done
    for((tmp=$3/2;tmp>0;tmp--)); do disk='('${disk}; done
    tput setab $(($4%8+1))
    tput cup $2 $(($1-$3/2))
    echo -n "$disk"
    tput sgr0
}

destroydisk()
## 将中心位置为 ($1,$2),长度为 $3 的盘子销毁
{
    tput cup $2 $(($1-$3/2))
    empty=''
    for((tmp=$3;tmp>0;tmp--)); do empty=' '$empty; done
    echo -n "$empty"
    tput cup 0 0
}

moving()
## 将第 $5 个盘子从 ($1,$2) 移动到 ($3,$4)
{
    verticaltop=$(($groundat-1-$disks))
    horizspace=$(($3-$1))
    x=$1
    y=$2
    length=$((2+($5-1)*2))

    while(($y>=$verticaltop))
    do
 destroydisk $x $y $length
 y=$(($y-1))
 drawdisk $x $y $length $5
# sleep 0.5
    done

    sign=$(($horizspace<0?-1:1))
    while(($3-$x!=0))
    do
 destroydisk $x $y $length
 x=$(($x+1*$sign))
 drawdisk $x $y $length $5
# sleep 0.5
    done

    while(($y<$4))
    do
 destroydisk $x $y $length
 y=$(($y+1))
 drawdisk $x $y $length $5
# sleep 0.5
    done
}

hanoi()
{
    bool1=$(($disks%2))
   
    for((s=1;s<2**$disks;s++))
    { 
 index=$(factor $s | sed 's/.*: //;s/2 /2\n/g' | grep '^2$' -c)
 step=$(((s/(2**$index)+1)/2))
 index=$(($index+1))
 bool2=$(($index%2))
 fromtonum=''

        ## 求出第 s 步时,应将第 $index 个盘子从底座(1或2或3)移动到底座(1或2或3) 
 if(($bool1==$bool2))
 then
     case $(($step%3)) in
  1)
      fromtonum='1 3';;
  2)
      fromtonum='3 2';;
  0)
      fromtonum='2 1';;
     esac
 else
     case $(($step%3)) in
  1)
      fromtonum='1 2';;
  2)
      fromtonum='2 3';;
  0)
      fromtonum='3 1';;
     esac
 fi
 fromtonum=${fromtonum}" $index"

 ## 按照盘子序号和底座序号计算出移动的源及目的的坐标 coordinate
 _getcoordinat $fromtonum
 
 tput cup $groundat $(($colums+1+$colums/2))
 tput setab 3
 tput setaf 0
 echo $s
 tput sgr0

 ## 移动盘子
 moving $coordinate
 
    }

}

_getcoordinat()
## 第 $3 个盘子要从底座 $1 移动到底座 $2,据此计算出移动的源坐标和目的坐标
{
    case "$1" in
 1)
     coordinate="$sourcex $sourcey "
     sourcey=$(($sourcey+1));;
 2)
     coordinate="$aidx $aidy "
     aidy=$(($aidy+1));;

 3)
     coordinate="$destinationx $destinationy "
     destinationy=$(($destinationy+1));;
    esac

    case "$2" in
 1)
     sourcey=$(($sourcey-1))
     coordinate=${coordinate}"$sourcex $sourcey";;
 2)
     aidy=$(($aidy-1*$aidfirst))
     coordinate=${coordinate}"$aidx $aidy"
     aidfirst=1;;
 3)
     destinationy=$(($destinationy-1*$destinationfirst))
     coordinate=${coordinate}"$destinationx $destinationy"
     destinationfirst=1;;
    esac

    coordinate=${coordinate}" $3"
}

################ 开始 #####################
[[ -z "$disks" || "$disks" =~ [^[:digit:]] ]] && echo "usage: ""$usage" && exit
(( $disks+2>$groundat || (2+($disks-1)*2)*3+4>$(tput cols))) && echo "screen is too small, reseize it" && exit
tput clear
tput civis
initdraw
stty -echo

sleep 0.5
for((i=5;i>=0;i--))
do
    tput cup $(($(tput lines)/2)) $(($(tput cols)/2-2))
    tput setab $i
    echo ' '$i' '
    sleep 0.6
    tput sgr0
    tput cup $(($(tput lines)/2)) $(($(tput cols)/2-2))
    echo '   '
    sleep 0.4
done
tput sgr0
tput cup $(($(tput lines)/2)) $(($(tput cols)/2))
echo " "
hanoi

stty echo
tput reset
tput sgr0
tput cup $(tput lines) 0
tput cnorm


TAG: Shell

 

评分:0

我来说两句

Open Toolbar