php 推箱子(sokoban)最短路径实现

借鉴python 实现思想转成php

最短路径:
u表示向上,d表示向下,l表示向左,r表示向右。

大写表示人推着箱子一起动,小写表示人自己走。

以下为实现代码 都写在注释里面


<?php
/*
 *推箱子游戏求解算法
 */
	class Sokoban {

		public $line = null;
		
		public $sta=''; 	//开始状态
		public $en ='';	       //结束的状态
		public $col = null;     //最好列宽相等
		
		public $px = -1;
		public $py = -1;
		
		public $paths = []; 	//记录推箱子的最短路径
		public $len = -1; 	//记录最短路径的长度

        public function __construct($line,$col){
                 $this->line = $line;
                 $this->col  = $col;
        }


		public function start()
		{
			/*
			 *1. 获取sta开始状态和en结束的状态
			 *2. 获得人的起始位置 px , py
			 *3. 
			 */
			$mp = [];
			for ($i =0; $i < 100; $i+=10){
				array_push($mp,substr($this->line,$i,10));
			}
            $len1 = strlen($this->line);
            $line = $this->line;

            for($i = 0; $i < $len1; $i++ ){
                    $cx = floor($i / 10);
                    $cy =  $i % 10;
                    if(substr($line,$i,1) == 4){
                            $this->px = $cx;
                            $this->py = $cy;
                    }
            }
            # 现在只需要把sta开始的状态中的2位置移动到en的3的位置即满足条件
            $staDic = [0,0,2,0,4];
            $enDic = [0,1,0,3,0];

            //var_dump($len1);
            $static = 0;
            $entic = 0;
            for($i = 0; $i < $len1; $i++){
                $j = substr($line, $i,1);
      
                $this->sta .= $staDic[$j];
                $this->en .= $enDic[$j];
            
            }
           // var_dump($this->sta);
           // var_dump($this->en);
                      
		}
		public function Isok($sta){

            $len = strlen($sta);
            $len0 = strlen($this->en);
            $endlen = $len > $len0 ? $len0 : $len;
            // var_dump('sta:',$sta);
            // var_dump('en:',$this->en);
            
            for($i = 0; $i < $endlen; $i++){
                    $s = substr($sta, $i,1);
                    $e = substr($this->en, $i,1);
                    if($e == '3' && $s != '2'){
                            
                            return false;
                    }
            } 
            return true;
		}

		//获取最短的路径保存到paths中
		public function run(){
		// 4个方向,小写代表只是人移动,大写表示人推着箱子一起移动
            $dirs = [[-1,0,'u','U'],[1,0,'d','D'],[0,1,'r','R'],[0,-1,'l','L']];
            //状态包括字符串表示的当前状态、当前的路径、当前人的位置
            $states = [[$this->sta,'',$this->px,$this->py]];
            // 定义数组 剔除重复的数据
            $visi = [];
            $visi[$this->sta] = 1;

            $s_len = 1000;
            //var_dump($visi);
            //$this->dd(count($states));
            while (count($states) > 0){
            	$sta = $states[0][0];
            	$path = $states[0][1];
            	$px = $states[0][2];
            	$py = $states[0][3];
            	// 4的装填位置 也就是人在哪
            	$ppos = $px * $this->col  + $py;
                   
                $states = array_slice($states, 1);

            	if(strlen($path) > $s_len ){
            		break;
            	}
            	//保存路径到path中
            	if ($this->Isok($sta)){     
            		if($this->len == -1  || strlen($path) <= $this->len){       
            			array_push($this->paths, $path);            
            			$this->len = strlen($path);
            		}
                    continue;
            	}
                    
            	foreach($dirs  as $k => $v){
                       
            		$cx = $px + $v[0];
            		$cy = $py + $v[1];
                            
            		$pos = $cx * $this->col + $cy;
                     //var_dump($pos);
            		//4 挨着的位置
            		$nx = $px + 2 * $v[0];
            		$ny = $py + 2 * $v[1];
                           
            		//人挨着的装填位置
            		$npos = $nx * $this->col + $ny;
                // var_dump($npos);

                // continue;

                if(!($nx >= 0 && $nx < $this->col && $ny >= 0 && $ny < $this->col )){
                        var_dump("难道是这个里面吗");
                       continue; 
                }

                //
                //var_dump(substr($sta, $pos,1),substr($this->en, $pos,1));
                if($sta["$pos"] == '2' && $sta["$npos"] == '0' && substr($this->en, $npos,1) != '1'){
                       $sta["$ppos"] = 0;
                       $sta["$pos"]  = 4;
                       $sta["$npos"] = 2;

                       //var_dump($digits);exit;
                       $new_sta= $sta;
                       // var_dump($new_sta);
                       // var_dump($visi);
                       // var_dump(!array_key_exists("$new_sta", $visi));exit;

                       if(!array_key_exists("$new_sta", $visi)){
                          $visi["$new_sta"] = 1;
                          // var_dump($cx);
                          // var_dump(["$new_sta",$path.$v[3],$cx,$cy]);exit;
                          array_push($states,["$new_sta",$path.$v[3],$cx,$cy]);
                          //var_dump("176",$states);

                       	}

            	    }elseif(substr($sta, $pos,1) == '0' && substr($this->en, $pos,1) != '1'){

                           $sta["$ppos"] = 0;
                           $sta["$pos"]  = 4;
                           $new_sta = $sta;
                          
                            if(!array_key_exists("$new_sta", $visi)){
                              $visi["$new_sta"] = 1;
                              array_push($states,["$new_sta",$path.$v[2],$cx,$cy]);
                           }
                    }       


                }
            }


		}


	}
			
   $map = '1111111111111111111111100011111110221111111420111111111001111111300111111330011111111111111111111111';
   $col = 10;	
   $obj = new Sokoban($map,$col);
   $obj->start();
   $obj->run();
   var_dump($obj->paths);

       

       



	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值