Perl看完這個,再不敢說自己會玩貪吃蛇


  某天閑逛時看見一副動圖:

    

  真的是非常貪吃,各種拐彎各種吃,感覺十分有趣。

  用Perl來實現自動吃滿,蓄謀已久,之前的字符貪吃蛇、深度優先算法、A*算法,都是為此篇做鋪墊。

  

  那么,怎樣讓蛇不吃到自己呢?

  1、讓蛇按照我們設計好的路線行進,在一個N*M(N、M均為偶數,奇數不討論)的游戲區域,設計好路線如下:

    

    當花兒謝了,果子熟透,春夏秋冬一個輪回后,蛇終於吃滿了。。。

  2、假設蛇總是追着自己的尾巴跑,那么永遠不會死;然而,這沒什么鳥用,我們需要的是一條“貪吃”的蛇。

  3、在2上稍稍改進,吃完食物后再追着尾巴,嗯,已經很接近了。但是問題來了:怎么吃到食物?吃到食物后被圍死怎么辦?

 

  其實我的想法就是解決3中的問題:

  1、使用A*算法計算到食物的最短路徑

  2、模擬吃到食物后的場景,能否回到蛇尾;能--吃食物,不能--繼續追着尾巴,重復1、2

  3、如果無法回到尾巴,保持當前移動方向(認命吧,要掛的節奏)

  偽碼如下:

#    while( 存活 && 未吃滿){
#        計算蛇頭到食物的路徑
#        if( 有蛇頭到食物的路徑 ){
#            模擬吃到食物后的場景
#            計算是否有路徑到達蛇尾
#            if( 有路徑 ){        
#                執行蛇頭到食物的路徑
#            }
#            else{  # 沒有到蛇尾的路徑
#                next
#            }
#        }
#       找出當前可行方向追逐蛇尾
#    }
#    if(吃滿){
#        顯示:祝賀
#    }
#    else{
#        顯示:wtf ?
#    }

  模擬吃到食物后的場景:使用A*計算路徑,並創建一條“影子蛇”用來模擬吃到食物后的狀態

  計算是否有路徑到達蛇尾:和“追逐蛇尾”方法類似,也可使用A*

  找出當前可行方向追逐蛇尾:

    由於移動時尾巴會變動,未嘗試A*,使用指定方向深度優先搜索算法

    偽碼如下:

sub trace_tail{
    @moves=grep{可行};
    foreach(@moves){
        next if(下一步越界 || 吃到自己)
        unshift @feasible,$_ if(遠離食物)
        其他方向 push @feasible
    }
    foreach(@feasible){
        next_move=cur_move+$_
        指定方向優先探索
        next_move的反向上一步設為不可通過
        每走一步,從蛇尾開始設未經過;每原路返回一次,當前位置的蛇身設不可通過
        if(有路徑)return next_move
    }
    執行完畢無路徑,return cur_move
}

  

  運行如下:

    

  實際上還是會被困死,雖然追逐尾巴時避開食物優先,但唯一路徑可能會出現食物,不吃也得吃了,如下:

    

  代碼較亂,准備加好注釋后貼上來。

  有很大的優化空間,等有時間再慢慢搞,代碼:

use strict;
use 5.01;
use Time::HiRes qw/sleep/;
use Term::ReadKey;
use constant {WIDTH=>12,HEIGHT=>8,DEBUG=>0};
my @bg=();
my @snake=();
my @head2food=();
my @food_coordinate=();  # 保存食物坐標
my ($score,$food,$speed,$alive,$head2tail,$head_move)=(0,0,1,1,0,4); # 得分、食物、速度、存活、能否回到尾巴的標志、初始移動方向
my $full=(WIDTH-2)*(HEIGHT-2);
my %opposite=( 1=>3,
              2=>4,
              3=>1,
              4=>2, );  # 對立的移動方向,禁止反向移動
my @uldr=( 0,[-1,0],[0,-1],[1,0],[0,1], ); # 上、左、下、右
############################################################################################
&init;
#my $move=&manual_move(4);  # 開始向右移動
my $move=&smart_move($head_move);$speed=9;
while( $alive && @snake < $full ){        
    @head2food=&head_to_food($head_move,@snake);  # 計算蛇頭到食物的路徑
    if( $head2food[0] != 0 ){                      # 有路徑
        $head2tail=&head_to_tail(\@snake,\@head2food);    # 判斷吃到食物后是否能回到尾巴
        if($head2tail){        # 能回到尾巴,吃食物
            foreach( @head2food ){
                $head_move=$_;
                $move->($head_move);
                &check_head;
                if($alive){
                    &show;
                    sleep (1-$speed*0.1);
                }
            }
        }
    } 
    $head_move=&trace_tail($head_move,@snake);        # 找出當前可行方向追逐蛇尾
    $move->($head_move);
    &check_head;
    if($alive){
        &show;
        sleep (1-$speed*0.1);
    }
}
if(@snake==$full){
    print "\ncongratulations!\n";  # 蛇占滿游戲區時顯示
}
else{
    print "\nWTF?\n";
}
############################################################################################
sub init{
    my $y=int(HEIGHT/2);
    for(my $y=0;$y<HEIGHT;$y++){
        for( my $x=0 ; $x<WIDTH ; $x++ ){
            if( $y == 0 || $y == HEIGHT-1 ||
                $x == 0 || $x == WIDTH-1  ){
                $bg[$y][$x] = '*';
            }
            else{
                $bg[$y][$x] = ' ';
            }
        }
    }
    @{$bg[$y]}[1,2]=('#','@'); # 初始蛇身位置
    @snake=( [$y,2],[$y,1], ); # 保存蛇身坐標
    &make_food;  # 產生食物
}
############################################################################################
sub show{
    system("cls") unless(DEBUG);
    print "your score : $score\n";
    print "current speed : ",$speed,"\n\n";
    print @$_,"\n" foreach(@bg);
}
############################################################################################
sub smart_move{
    my $move_direct=shift;
    sub{  # 閉包,傳入初始移動方向
        $move_direct=shift;
        unshift @snake,[$snake[0][0]+$uldr[$move_direct][0],$snake[0][1]+$uldr[$move_direct][1]];
    }
}
############################################################################################
sub head_to_food{  # 蛇頭到食物
    my $head_shadow=shift;
    my @snake_ghost=@_; 
    my @bg_ghost=@bg;
    my @path=&a_star( [ $snake_ghost[0][0],$snake_ghost[0][1] ],
                      [ $food_coordinate[0],$food_coordinate[1] ],\@bg_ghost ); # A*算法,傳遞起點、終點、蛇當前坐標指針
    return @path;
}
############################################################################################
# 追逐尾巴,傳遞當前移動方向、蛇身所有坐標
sub trace_tail{
    print "call trace_tail\n" if DEBUG;
    my $cur_move=shift;  # 當前移動方向
    my @snake_ghost=@_;
    my @path=();
    my $cur_position=[ $snake_ghost[0][0],$snake_ghost[0][1] ];  # 起點
    print "    cur_position:",$snake_ghost[0][0],",",$snake_ghost[0][1],"\n" if DEBUG;    
    my $end=[ $snake_ghost[-1][0],$snake_ghost[-1][1] ];  # 終點
    my $tail=$#snake_ghost;  # 蛇長度
        
    my @bg_ghost=map{ [ split('','0'x WIDTH) ] }0..(HEIGHT-1);  # 0--未經過 1--已走 2--不可通過
    map{ my $y=$_;map { $bg_ghost[$y][$_] = ( $bg[$y][$_] eq '*' )?2:0 }0..$#{$bg[0]} }0..$#bg;
    map{ $bg_ghost[ $snake_ghost[$_][0] ][ $snake_ghost[$_][1] ] = 2 } 1..$#snake_ghost;  # 蛇身不可通過
    my @feasible=();
    my @next_moves=grep{ ! /$opposite{$cur_move}/ }values %opposite;  # 取出除反方向的可行方向
    my $weight=0;
    foreach(@next_moves){
        $weight=1;
        my $next_move=$_;
        my ($y,$x)=( $snake_ghost[0][0]+$uldr[$next_move][0],$snake_ghost[0][1]+$uldr[$next_move][1] );
        # 防止越界
        if( $y < 1 || $y > HEIGHT-2 || $x < 1 || $x > WIDTH-2){
            $weight=0;
            next;
        }
        
        # 防止吃到自己
        foreach(0..$#snake_ghost){
            if( $y == $snake_ghost[$_][0] && $x == $snake_ghost[$_][1]  ){
                $weight=0;
                last;
            }
        }
        if($weight){
            if( abs($y - $food_coordinate[0]) > abs($snake_ghost[0][0] - $food_coordinate[0]) ||
                abs($x - $food_coordinate[1]) > abs($snake_ghost[0][1] - $food_coordinate[1]) ){
                    unshift @feasible,$next_move;  # 遠離食物放到數組頭
            }
            else{
                push @feasible,$next_move;  # 其他路徑放在末尾
            }
        }
    }
    print "    feasible:@feasible\n" if DEBUG;
    foreach(@feasible){
        @path=();
        $cur_move=$_;
        print "    cur_move:$cur_move\n" if DEBUG;
    my @one234 = ();  # 將當前移動方向設置為初始方向
    given($cur_move){
        when(1){ @one234 = @uldr;}
        when(2){ @one234 = @uldr[0,2,3,4,1]; }
        when(3){ @one234 = @uldr[0,3,4,1,2]; }
        when(4){ @one234 = @uldr[0,4,1,2,3]; }
    }
#################################################################
# 指定方向深度搜索
    my ($step,$p_step)=(0,'');    
    do{
        if($bg_ghost[ $cur_position->[0] ][ $cur_position->[1] ] == 0 ){
            $bg_ghost[ $cur_position->[0] ][ $cur_position->[1] ]=1;
             # 當前移動方向的反方向點不可經過,即禁止回退
            $bg_ghost[ $snake_ghost[0][0]+$uldr[$opposite{$cur_move}]->[0] ][ $snake_ghost[0][1]+$uldr[$opposite{$cur_move}]->[1] ] = 2;
            $bg_ghost[ $snake_ghost[0][0] ][ $snake_ghost[0][1] ] = 2;  # 當前點不可返回
            if( $cur_position->[0] == $food_coordinate[0] && $cur_position->[1] == $food_coordinate[1] ){
                push @snake_ghost,[ $snake_ghost[-1][0],$snake_ghost[-1][0] ];
            }
            $path[$step]={step=>$step,
                          coordinate=>[$cur_position->[0],$cur_position->[1]],
                          direct=>1,
                         };
            $path[$step]->{direct}=4 if($step == 0);  # 起點不可再做起點
            print "    path[$step]:$path[$step]:",$path[$step]->{step},"\n" if DEBUG;
            if(DEBUG){
                        print @$_,"\n" foreach(@bg_ghost);
                    }
            if( $cur_position->[0]==$end->[0] && $cur_position->[1]==$end->[1]){
                my @arr=('A'..'Z','a'..'z');
                foreach(0..$#path){
                    $bg_ghost[ $path[$_]->{coordinate}->[0] ][ $path[$_]->{coordinate}->[1] ] = $arr[$_];
                }
                print "    trace_tail: return $cur_move\n" if DEBUG;
                return $cur_move;  # 有可行方向,返回
            }
            $step++;
            if($step>1 && $step<=$#snake_ghost){
            $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] = 0 
                if ( $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] == 2 );  # 每移動一次,將蛇尾所在坐標設未經過
            $tail=($tail>0)?($tail-1):1;
            }  
            $cur_position=[ $path[$step-1]->{coordinate}->[0]+$one234[1]->[0],
                            $path[$step-1]->{coordinate}->[1]+$one234[1]->[1] ];
            print "    (y,x):(",$cur_position->[0],",",$cur_position->[1],")\n" if DEBUG;
        }
        else{
            if(@path){
                $p_step=pop(@path);
                while($p_step->{direct}==4 && (@path)){
                    $bg_ghost[ $p_step->{coordinate}->[0] ][ $p_step->{coordinate}->[1] ] = 2;
                    $p_step=pop(@path);
                    $step--;

                    $tail=($tail<$#snake_ghost-1)?($tail+1):$#snake_ghost-1; 
                    $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] = 2 
                if ( $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] == 0 );  # 每回退一次,將蛇尾所在坐標設為不可通過

                }
                if($p_step->{direct}<4){
                    $p_step->{direct}++;
                    print "        ",$p_step->{step},":p_step->{direct}:",$p_step->{direct},"\n" if DEBUG;
                    push @path,$p_step;
                    my @temp=@{$p_step->{coordinate}}[0,1];
                    $cur_position = [ $temp[0]+$one234[$p_step->{direct}]->[0],
                                      $temp[1]+$one234[$p_step->{direct}]->[1] ];
                    print "        (y,x):(",$cur_position->[0],",",$cur_position->[1],")\n" if DEBUG;
                }
            }
        }
    }while(@path);
# 指定方向深度搜索結束
#################################################################
}
    print "    cur_move:$cur_move  trace_tail:return fail\n" if DEBUG;
    return $cur_move;  # 沒有到尾巴的可行方向了,准備認命
}
#######################################################################################
# 能否到尾巴位置,能返回1,否則返回0
# 算法大致與 trace_tail 相同
sub head_to_tail{
    print "call head_to_tail\n" if DEBUG;
    my ($p_snake,$p_path)=@_;
    my @snake_ghost=@$p_snake;
    my @path=@$p_path;
    my $cur_move=$path[-1];
    my @arr=();
    foreach(0..$#path){
        my ($y,$x)=( $snake_ghost[0][0]+$uldr[ $path[$_] ]->[0],$snake_ghost[0][1]+$uldr[ $path[$_] ]->[1] );
        unshift @snake_ghost,[$y,$x];
        pop @snake_ghost if($_ < $#path);
    }  # 影子蛇先行,吃到食物后的狀態
    @path=();
    my $cur_position=[ $snake_ghost[0][0],$snake_ghost[0][1] ];
    print "    cur_position:",$snake_ghost[0][0],",",$snake_ghost[0][1],"\n" if DEBUG;    
    my $end=[ $snake_ghost[-1][0],$snake_ghost[-1][1] ];
    my $tail=$#snake_ghost;
        
    my @bg_ghost=map{ [ split('','0'x WIDTH) ] }0..(HEIGHT-1);  # 0--未經過 1--已走 2--不可通過
    map{ my $y=$_;map { $bg_ghost[$y][$_] = ( $bg[$y][$_] eq '*' )?2:0 }0..$#{$bg[0]} }0..$#bg;
    map{ $bg_ghost[ $snake_ghost[$_][0] ][ $snake_ghost[$_][1] ] = 2 } 1..$#snake_ghost;  # 蛇身不可通過
        my @feasible=();
    my @next_moves=grep{ ! /$opposite{$cur_move}/ }values %opposite;
    my $weight=0;
    foreach(@next_moves){
        $weight=1;
        my $next_move=$_;
        my ($y,$x)=( $snake_ghost[0][0]+$uldr[$next_move][0],$snake_ghost[0][1]+$uldr[$next_move][1] );
        # 防止越界
        if( $y < 1 || $y > HEIGHT-2 || $x < 1 || $x > WIDTH-2){
            $weight=0;
            next;
        }
        
        # 防止吃到自己
        foreach(0..$#snake_ghost){
            if( $y == $snake_ghost[$_][0] && $x == $snake_ghost[$_][1] ){
                $weight=0;
                last;
            }
        }
        if($weight){
            push @feasible,$next_move;  # 路徑隨意放
        }
    }
    print "    feasible:@feasible\n" if DEBUG;
    foreach(@feasible){
        @path=();
        $cur_move=$_;
    my @one234 = ();  # 將當前移動方向設置為初始方向
    given($cur_move){
        when(1){ @one234 = @uldr;}
        when(2){ @one234 = @uldr[0,2,3,4,1]; }
        when(3){ @one234 = @uldr[0,3,4,1,2]; }
        when(4){ @one234 = @uldr[0,4,1,2,3]; }
    }
#################################################################
# 指定方向深度搜索
    my ($step,$p_step)=(0,'');    
    do{
        if($bg_ghost[ $cur_position->[0] ][ $cur_position->[1] ] == 0 ){
            $bg_ghost[ $cur_position->[0] ][ $cur_position->[1] ]=1;
            $bg_ghost[ $snake_ghost[0][0]+$uldr[$opposite{$cur_move}]->[0] ][ $snake_ghost[0][1]+$uldr[$opposite{$cur_move}]->[1] ] = 2;
            $bg_ghost[ $snake_ghost[0][0] ][ $snake_ghost[0][1] ] = 2;  # 當前點不可返回
            $path[$step]={step=>$step,
                          coordinate=>[$cur_position->[0],$cur_position->[1]],
                          direct=>1,
                         };
            $path[$step]->{direct}=4 if($step == 0);  # 起點不可再做起點
            print "    path[$step]:$path[$step]:",$path[$step]->{step},"\n" if DEBUG;
            if( $cur_position->[0]==$end->[0] && $cur_position->[1]==$end->[1]){
                my @arr=('A'..'Z','a'..'z');
                foreach(0..$#path){
                    $bg_ghost[ $path[$_]->{coordinate}->[0] ][ $path[$_]->{coordinate}->[1] ] = $arr[$_];
                }
                print "    head_to_tail: return 1\n" if DEBUG;
                return 1;
            }
            $step++;
            if($step>1 && $step<=$#snake_ghost){
            $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] = 0 
                if ( $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] == 2 );  # 每移動一次,將蛇尾所在坐標設未經過
            $tail=($tail>0)?($tail-1):1;
            }  
            $cur_position=[ $path[$step-1]->{coordinate}->[0]+$one234[1]->[0],
                            $path[$step-1]->{coordinate}->[1]+$one234[1]->[1] ];
            print "    (y,x):(",$cur_position->[0],",",$cur_position->[1],")\n" if DEBUG;
        }
        else{
            if(@path){
                $p_step=pop(@path);
                while($p_step->{direct}==4 && (@path)){
                    $bg_ghost[ $p_step->{coordinate}->[0] ][ $p_step->{coordinate}->[1] ] = 2;
                    $p_step=pop(@path);
                    $step--;
                    $tail=($tail<$#snake_ghost)?($tail+1):$#snake_ghost; 
                    $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] = 2 
                if ( $bg_ghost[ $snake_ghost[$tail][0] ][ $snake_ghost[$tail][1] ] == 0 );  # 每回退一次,將蛇尾所在坐標設為不可通過
                    if(DEBUG){
                        print @$_,"\n" foreach(@bg_ghost);
                    }                        
                }
                if($p_step->{direct}<4){
                    $p_step->{direct}++;
                    print "        ",$p_step->{step},":p_step->{direct}:",$p_step->{direct},"\n" if DEBUG;
                    push @path,$p_step;
                    my @temp=@{$p_step->{coordinate}}[0,1];
                    $cur_position = [ $temp[0]+$one234[$p_step->{direct}]->[0],
                                      $temp[1]+$one234[$p_step->{direct}]->[1] ];
                    print "        (y,x):(",$cur_position->[0],",",$cur_position->[1],")\n" if DEBUG;
                }
            }
        }
    }while(@path);
# 指定方向深度搜索結束
#################################################################
    }
    print "    head_to_tail:return 0\n" if DEBUG;
    return 0;    
}
#######################################################################################
sub caclulate_cost{
    my ($sp,$ep)=@_;
    return abs($sp->[0] - $ep->[0]) + abs($sp->[1] - $ep->[1]);
}
#######################################################################################
# A*算法
sub a_star{
    print "call a_star\n" if DEBUG;
    my $start=shift;  # 起點
    my $end=shift;  # 終點
    my $p_arr=shift;
    my @bg_ghost=@{$p_arr};
    
    my @path=();  # 存放步數的數組    
    my @open_close=();
    my ($step,$p_step,$p_gh)=(0,'','');  # 步數、指向數組元素的指針、指向open_close元素的指針
    map{ my $y=$_;map { $open_close[$y][$_]->{flag} = ( $bg_ghost[$y][$_] eq '#' || $bg_ghost[$y][$_] eq '*')?2:0 }0..$#{$bg_ghost[0]} }0..$#bg;  # 障礙物設置不可通過
    
    $path[$step]={ coordinate=>[$start->[0],$start->[1]],
                   cost=>0,
                   next_cost=>&caclulate_cost( $start,$end ),
                   previous=>0,
                  };
    $path[$step]->{actual_cost}=$path[$step]->{cost} + $path[$step]->{next_cost};
    $open_close[ $start->[0] ][ $start->[1] ]->{point}='';
    while(@path){
        $p_step=pop(@path);
        print "  step:$step,p_step:$p_step\n" if DEBUG;
        if( $p_step->{coordinate}->[0] == $end->[0] &&
            $p_step->{coordinate}->[1] == $end->[1] ){
            my @arr=();
            my @temp=();
            while($p_step){
                push @temp,$p_step->{coordinate};
                $p_step=$p_step->{previous};
            }
            @temp=reverse(@temp);
            foreach(0..$#temp-1){
                my $line=($temp[$_+1][0]-$temp[$_][0])."a".($temp[$_+1][1]-$temp[$_][1]);
                given($line){
                    when('-1a0'){ push @arr,1 ;}
                    when('0a-1'){ push @arr,2 ;}
                    when('1a0') { push @arr,3 ;}
                    when('0a1') { push @arr,4 ;}
                }  # 從父節點回溯,獲取每一步移動方向
            }
            return @arr;
        }
        $step++;
        for(my $cnt=1;$cnt<=4;$cnt++){
            my $y= $p_step->{coordinate}->[0]+$uldr[$cnt][0] ;
            my $x= $p_step->{coordinate}->[1]+$uldr[$cnt][1] ;
            print "    ($p_step->{coordinate}->[0],$p_step->{coordinate}->[1])+($uldr[$cnt][0],$uldr[$cnt][1]),(y,x)=($y,$x)\n" if DEBUG;
            next if( $open_close[$y][$x]->{flag} == 2 ||
                     $y < 1 || $y > HEIGHT-2 || $x < 1 || $x > WIDTH-2 );
            
            if( $open_close[$y][$x]->{flag} == 0 ){                
                $open_close[$y][$x]->{flag}=1;
                $open_close[$y][$x]->{point}=$p_step;
                my $px={  coordinate=>[$y,$x],
                                   cost=>$p_step->{cost}+1,
                                   next_cost=>&caclulate_cost( [$y,$x],$end ),
                                   previous=>$p_step,
                               };
                $px->{actual_cost}=$px->{cost} + $px->{next_cost};
                push @path,$px;
            }
            else{
                $p_gh=$open_close[$y][$x]->{point};
                print "      p_gh:$p_gh\n" if DEBUG;
                if($p_gh && $p_step->{cost}+1 < $p_gh->{cost} ){
                    print "      $p_step->{cost},$p_gh->{cost}\n" if DEBUG;
                    $p_gh->{cost}=$p_step->{cost}+1;
                    $p_gh->{previous}=$p_step;
                    $p_gh->{actual_cost}=$p_gh->{cost}+$p_gh->{next_cost};
                }
            }
        }    
        $open_close[ $p_step->{coordinate}->[0] ][ $p_step->{coordinate}->[1] ]->{flag}=1;
        @path=sort{$b->{actual_cost}<=>$a->{actual_cost}}@path;
    }
    print "    a_star: return 0\n" if DEBUG;
    return 0;
}
#######################################################################################
sub manual_move{
    # 閉包,為了傳入初始移動方向
    my $move_direct=shift;
    sub{
        ReadMode 2;
        my $key=ReadKey(-1);
        $key=~tr/a-z/A-Z/ if $key;
        given($key){
            # 不允許反向移動
            when('W'){ $move_direct = ( 3 == $move_direct )? 3 : 1 ; }
            when('A'){ $move_direct = ( 4 == $move_direct )? 4 : 2 ; }
            when('S'){ $move_direct = ( 1 == $move_direct )? 1 : 3 ; }
            when('D'){ $move_direct = ( 2 == $move_direct )? 2 : 4 ; }
            default { $move_direct; }
        }
        unshift @snake,[$snake[0][0]+$uldr[$move_direct][0],$snake[0][1]+$uldr[$move_direct][1]];
    }
}
#######################################################################################
sub make_food{
    if(@snake < $full){
        my @empty_points=();
        foreach(1..$#bg-1){  
            my $y=$_;
            foreach(1..$#{$bg[0]}-1){
                push @empty_points,[$y,$_] if($bg[$y][$_] eq ' ');
            }
        }  # 找出所有空的坐標點,存入@empty_points數組
        my $num=int( rand( scalar(@empty_points) ) );  # 隨機取出@empty_points下標
        my ($y,$x)=@{ $empty_points[$num] }[0,1];
        $bg[$y][$x]='O';
        @food_coordinate=($y,$x);
        $food=1;
    }
}
#######################################################################################
sub check_head{
    # 蛇身超出范圍
    if($snake[0][0] < 1 || $snake[0][0] > HEIGHT-2 || 
       $snake[0][1] < 1 || $snake[0][1] > WIDTH-2 ){
            $alive=0;
    }
    # 蛇吃到自己
    if(@snake>3){
        foreach(1..$#snake){
            if($snake[0][0] == $snake[$_][0] &&  $snake[0][1] == $snake[$_][1]){
                $alive=0;
            }
        }
    }
    # 移動
    if($bg[$snake[0][0]][$snake[0][1]] eq ' '){
        $bg[$snake[0][0]][$snake[0][1]]='@';
    }
    # 吃到食物
    if($bg[$snake[0][0]][$snake[0][1]] eq 'O'){
        $bg[$snake[0][0]][$snake[0][1]]='@';
        $score++;
        $food=0;
        &make_food;
        push @snake,[$snake[-1][0],$snake[-1][1]];  # 新的蛇身放在尾部
    }
    $bg[$snake[-1][0]][$snake[-1][1]]=' ';  # 先清除尾巴顯示
    pop @snake;  # 去掉尾巴
    map{$bg[$snake[$_][0]][$snake[$_][1]]='#'}1..$#snake;  # 其他蛇身顯示
}
View Code

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM