Chinaunix首页 | 论坛 | 博客
  • 博客访问: 295445
  • 博文数量: 153
  • 博客积分: 3347
  • 博客等级: 中校
  • 技术积分: 1556
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-30 17:50
文章分类

全部博文(153)

文章存档

2013年(7)

2012年(21)

2011年(46)

2010年(16)

2009年(63)

我的朋友

分类: 系统运维

2011-09-15 09:54:10

上篇中提到的程序 运行时间太长,进行了一些优化

首先,减小了棋盘大小,五列,四行.

其次,使用了A*算法,每次优先搜索最有希望取胜的子树.

源代码如下:


//设置执行时间无限
set_time_limit ( 0 );

echo "begin!!!\n";

$width = 5; //棋盘宽度
$height = 4; //棋盘高度


//初始棋盘(x:0->6;y:0-5)
$board = array (array (' ', ' ', ' ', ' ', ' ', ' ' ), array (' ', ' ', ' ', ' ', ' ', ' ' ), array (' ', ' ', ' ', ' ', ' ', ' ' ), array (' ', ' ', ' ', ' ', ' ', ' ' ), array (' ', ' ', ' ', ' ', ' ', ' ' ), array (' ', ' ', ' ', ' ', ' ', ' ' ), array (' ', ' ', ' ', ' ', ' ', ' ' ) );

//当前棋手:红
$oper = 'R';

//计算最优结果
print process ( $oper, '' );

//计算当前状态最优结论
//返回值:1 我胜了, -1 无论如何,对方胜了, 0 最终会平局
function process($oper, $path) {
    global $board, $width, $height;
   
    //记录每一种落子方式的估计值及落子点
    $every = array ();
   
    //所有可能的落子处
    for($x = 0; $x < $width; $x ++) {
        //本列已经满
        if ($board [$x] [$height - 1] != ' ')
            continue;
           
        //找到本列的一个最低的落子位
        for($y = 0; $y < $height - 1; $y ++) {
            if ($board [$x] [$y] == ' ')
                break;
        }
       
        //计算4种直线方向的最大连续棋子数
        list ( $max, $sum ) = max4 ( $oper, $x, $y );
       
        //当前棋手有一种落子方法可以取胜,其它方法不用继续计算
        if ($max >= 4) {
            echo $path . ' ' . $oper . '!' . "\n";
            flush ();
            return 1;
        }
       
        //记录每一种落子时的估计值(八向合计再加上最大值,以增加最大值的作用)
        $every [] = array ($max + $sum, $x, $y );
    }
   
    //按估计值排序
    $every = sortArrayDesc ( $every );
   
    //按估计值先后开始尝试
    foreach ( $every as $k => $once ) {
        $x = $once [1];
        $y = $once [2];
       
        //落子,并计算对方的计算结果,之后恢复棋盘
        $board [$x] [$y] = $oper;
        $other = process ( $oper == 'R' ? 'G' : 'R', $path . $x );
        $board [$x] [$y] = ' ';
       
        //如果对方计算结果为1(取胜),那么表明自己不能在此落子
        if ($other == 1)
            continue;
           
        //如果对方计算结果为-1(必输),那么自己一定要在此落子
        if ($other == - 1)
            return 1;
    }
   
    //自己无论在哪里落子,都是平局
    return 0;
}

//根据数组中的第0个元素对整个二维数组进行从大到小排序(交换排序)
function sortArrayDesc($array) {
    $count = count ( $array );
    if ($count < 2) {
        return $array;
    }
    for($i = 0; $i < $count - 1; $i ++) {
        if ($array [$i] [0] < $array [$i + 1] [0]) {
            $temp = $array [$i];
            $array [$i] = $array [$i + 1];
            $array [$i + 1] = $temp;
        }
    }
    return $array;
}

//判断4种直线方向的最大连续长度(包括自己)
function max4($oper, $x, $y) {
    $n = max8 ( $oper, $x, $y, 0, 1 );
    $s = max8 ( $oper, $x, $y, 0, - 1 );
   
    $nw = max8 ( $oper, $x, $y, - 1, 1 );
    $se = max8 ( $oper, $x, $y, 1, - 1 );
   
    $w = max8 ( $oper, $x, $y, - 1, 0 );
    $e = max8 ( $oper, $x, $y, 1, 0 );
   
    $sw = max8 ( $oper, $x, $y, - 1, - 1 );
    $ne = max8 ( $oper, $x, $y, 1, 1 );
   
    $max = max ( array ($n + 1 + $s, $nw + 1 + $se, $w + 1 + $e, $sw + 1 + $ne ) );
    $sum = $n + $s + $nw + $se + $w + $e + $sw + $ne;
   
    //返回最大值及合计
    return array ($max, $sum );
}

//判断8种射线方向有几个连续同色棋子
function max8($oper, $x, $y, $x_offset, $y_offset) {
    global $board;
    for($i = 1; $i <= 3; $i ++) {
        $xx = $x + $x_offset * $i;
        $yy = $y + $y_offset * $i;
        if (! inRange ( $xx, $yy ))
            return $i - 1;
        if ($board [$xx] [$yy] != $oper)
            return $i - 1;
    }
    return 3;
}

//判断坐标是否在棋盘内
function inRange($x, $y) {
    global $width, $height;
    if ($x < 0 or $x >= $width or $y < 0 or $y >= $height)
        return false;
    return true;
}


阅读(606) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~