class Sudoku
{

    public $data = [
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]
    ];

    public $sudoku_map = [];
    public $stacks = [];
    public $resolved = false;
    public $data_map = [];
    public $orders = [];
    public $current_order = 0;
    public $steep_count = 0;
    public $max_count = 100000;

    public function getRandData()
    {
        try {
            for ($i = 0; $i < 9; $i++) {
                for ($j = 0; $j < 9; $j++) {
                    $this->data[$i][$j] = 0;
                }
            }

            $num = [1, 2, 3, 4, 5, 6, 7, 8, 9];

            for ($i = 0; $i < 9; $i++) {
                $index = random_int(0, count($num) - 1);

                $k = random_int(0, 8);

                $this->data[$i][$k] = $num[$index];

                unset($num[$index]);

                $num = array_values($num);

            }

            $this->data = Common::rotateArr(random_int(1, 4), $this->data);

            foreach ($this->data as $key => $item) {
                $this->data[$key] = array_values($item);
             }

            $this->data = Common::horizontalMirrorArr(random_int(0, 1), $this->data);
            $this->data = Common::verticalMirrorArr(random_int(0, 1), $this->data);

            $this->init();

            foreach ($this->sudoku_map as $key => $item) {
                $this->sudoku_map[$key] = array_values($item);
            }

            $this->sudoku_map = array_values(Common::rotateArr(random_int(1, 4), $this->sudoku_map));

            foreach ($this->sudoku_map as $key => $item) {
                $this->sudoku_map[$key] = array_values($item);
            }

            $this->sudoku_map = Common::horizontalMirrorArr(random_int(0, 1), $this->sudoku_map);
            $this->sudoku_map = Common::verticalMirrorArr(random_int(0, 1), $this->sudoku_map);

        } catch (\Exception $e) {
        }

        return $this->sudoku_map;
    }

    public function init()
    {
        $this->sudoku_map = $this->data;
        $this->stacks = [];
        $this->resolved = false;
        $this->data_map = [];
        $this->orders = [];
        $this->current_order = 0;
        $this->steep_count = 0;

        for ($i = 0; $i < 9; $i++) {
            for ($j = 0; $j < 9; $j++) {
                $node = $this->sudoku_map[$i][$j];
                if ($node === 0) {
                    $this->testMayFill($i, $j);
                }
            }
        }

        $data = [];

        foreach ($this->data_map as $key => $value) {
            $data[] = [
                'x' => explode('-', $key)[0],
                'y' => explode('-', $key)[1],
                'value' => $value
            ];
        }

        $this->orders = $data;

        $this->start();
    }

    public function start()
    {
        $this->stacks[] = $this->getFirstPoint();

        while ($this->resolved === false) {
            $this->steep_count++;

            if ($this->steep_count > $this->max_count) {
                $this->resolved = true;
                return;
            }

            $c_stack = $this->stacks[count($this->stacks) - 1];

            if ($c_stack['x'] === -1 && $c_stack['y'] === -1) {
                $this->resolved = true;
                return;
            }

            $valid = $this->testRules($c_stack);

            if ($valid) {
                $this->sudoku_map[$c_stack['x']][$c_stack['y']] = $c_stack['value'];
                $this->stacks[] = $this->getNextPoint();
            } else {
                $this->rollback();
            }

            usleep(100);
        }
    }

    public function rollback()
    {

        $current_stack = array_pop($this->stacks);
        $this->sudoku_map[$current_stack['x']][$current_stack['y']] = 0;
        $cOrder = $this->orders[$this->current_order];

        if ($current_stack['value'] === $cOrder['value'][count($cOrder['value']) - 1]) {
            $this->current_order--;
            $this->rollback();
        } else {
            $order_index = array_search($current_stack['value'], $cOrder['value']);
            $current_stack['value'] = $cOrder['value'][$order_index + 1];
            $this->stacks[] = $current_stack;
        }
    }

    public function getNextPoint()
    {
        $ret = [
            'x' => -1,
            'y' => -1,
            'value' => 1
        ];

        $this->current_order++;

        $next_value = isset($this->orders[$this->current_order]) ? $this->orders[$this->current_order] : '';

        if (!$next_value) {
            $this->resolved = true;
            return $ret;
        }

        $ret['x'] = $next_value['x'];
        $ret['y'] = $next_value['y'];
        $ret['value'] = $next_value['value'][0];

        return $ret;
    }

    public function testRules($stack)
    {
        $x = $stack['x'];
        $y = $stack['y'];
        $val = $stack['value'];

        $ret = true;

        for ($i = 0; $i < 9; $i++) {
            $node = $this->sudoku_map[$i][$y];
            if ($node === $val) {
                return false;
            }
        }

        for ($j = 0; $j < 9; $j++) {
            $node = $this->sudoku_map[$x][$j];
            if ($node === $val) {
                return false;
            }
        }
        $startX = floor($x / 3) * 3;
        $startY = floor($y / 3) * 3;

        for ($i = $startX; $i < $startX + 3; $i++) {
            for ($j = $startY; $j < $startY + 3; $j++) {
                $node = $this->sudoku_map[$i][$j];
                if ($node === $val) {
                    return false;
                }
            }
        }
        return $ret;
    }

    public function getFirstPoint()
    {
        $ret = [
            'x' => $this->orders[0]['x'],
            'y' => $this->orders[0]['y'],
            'value' => $this->orders[0]['value'][0]
        ];

        return $ret;
    }

    public function testMayFill($x, $y)
    {
        $may_fill_number = [1, 2, 3, 4, 5, 6, 7, 8, 9];

        shuffle($may_fill_number);

        for ($i = 0; $i < 9; $i++) {
            $node = $this->sudoku_map[$i][$y];

            if (in_array($node, $may_fill_number)) {
                unset($may_fill_number[array_search($node, $may_fill_number)]);
                $may_fill_number = array_values($may_fill_number);
            }
        }

        for ($i = 0; $i < 9; $i++) {
            $node = $this->sudoku_map[$x][$i];

            if (in_array($node, $may_fill_number)) {
                unset($may_fill_number[array_search($node, $may_fill_number)]);
                $may_fill_number = array_values($may_fill_number);
            }
        }

        $startX = floor($x / 3) * 3;
        $startY = floor($y / 3) * 3;

        for ($i = $startX; $i < $startX + 3; $i++) {
            for ($j = $startY; $j < $startY + 3; $j++) {
                $node = $this->sudoku_map[$i][$j];

                if (in_array($node, $may_fill_number)) {
                    unset($may_fill_number[array_search($node, $may_fill_number)]);
                    $may_fill_number = array_values($may_fill_number);
                }
            }
        }

        $this->data_map[$x . '-' . $y] = $may_fill_number;
    }

}

旋转数组

public static function rotateArr($count, $data)
{
    for ($i = 0; $i < $count; $i++) {
        $actual_data = [];
        for ($j = 0; $j < count($data); $j++) {
            for ($k = count($data[$j]) - 1; $k >= 0; $k--) {
                if (!isset($actual_data[$k])) {
                    $actual_data[$k] = [];
                }
                $actual_data[$k][count($data[$j]) - 1 - $j] = $data[$j][$k];
            }
        }
        $data = $actual_data;
    }

    foreach ($data as $key => $item) {
        ksort($item);
        $data[$key] = $item;
    }

    ksort($data);

    return $data;
}

镜像数组

public static function verticalMirrorArr($count, $data) {

    $actual_data = $data;

    for ($i = 0; $i < $count; $i++) {

        for ($j = 0; $j <= count($data) / 2; $j ++) {

            $actual_data[$j] = $data[count($data) - 1 - $j];
            $actual_data[count($data) - 1 - $j] = $data[$j];

        }

        $data = $actual_data;
    }

    return $actual_data;

}

public static function horizontalMirrorArr($count, $data) {

    $data = self::rotateArr(3, $data);

    $data = self::verticalMirrorArr(1, $data);

    $data = self::rotateArr(1, $data);

    foreach ($data as $key => $item) {
        $data[$key] = array_values($item);
    }

    return $data;

}

点赞(168) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部