davaxi/Takuzu

View on GitHub
src/Resolver.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

namespace Davaxi\Takuzu;
use Davaxi\Takuzu\ResolverMethod\RandomValue;

/**
 * Class Resolver
 * @package Davaxi\Takuzu
 */
class Resolver
{
    /**
     * @var bool
     */
    protected $resolved = false;

    /**
     * @var Grid
     */
    protected $originalGrid;

    /**
     * @var Grid
     */
    protected $resolvedGrid;

    /**
     * @var ResolverChain[]
     */
    protected $chains = array();

    /**
     * @var ResolverChain
     */
    protected $resolvedChain;

    /**
     * @var ResolverMethod[]
     */
    protected $resolverMethods = array(
        '\Davaxi\Takuzu\ResolverMethod\NoThreeSide',
        '\Davaxi\Takuzu\ResolverMethod\NoThreeCenter',
        '\Davaxi\Takuzu\ResolverMethod\CompleteEquality',
        '\Davaxi\Takuzu\ResolverMethod\NecessaryValues',
        '\Davaxi\Takuzu\ResolverMethod\NoPossibleRange',
    );

    /**
     * Resolver constructor.
     * @param Grid $grid
     */
    public function __construct(Grid $grid)
    {
        $this->originalGrid = $grid;
        $this->chains[] = new ResolverChain($grid);
        $this->resolved = $this->originalGrid->getChecker()->hasResolved();
        if ($this->resolved) {
            $this->resolvedGrid = $grid;
        }
    }

    /**
     * @return bool
     */
    public function hasResolved()
    {
        return $this->resolved;
    }

    /**
     * @return Grid
     */
    public function getResolvedGrid()
    {
        return $this->resolvedGrid;
    }

    /**
     * @param Grid $grid
     * @return ResolverMethod|null
     */
    protected function foundNextResolveGridMethod(Grid $grid)
    {
        foreach ($this->resolverMethods as $resolverMethodClass) {
            /** @var ResolverMethod $resolverMethod */
            $resolverMethod = new $resolverMethodClass($grid);
            if ($resolverMethod->found()) {
                return $resolverMethod;
            }
        }
        return null;
    }

    /**
     * @param Grid $grid
     * @param $value
     * @return ResolverStep
     */
    protected function generateTryResolverStep(Grid $grid, $value)
    {
        list($lineNo, $columnNo) = $grid->getFirstEmptyCasePosition();
        $resolverMethod = new RandomValue($grid);
        $resolverMethod->setValue($lineNo, $columnNo, $value);
        return $this->generateResolverStep($resolverMethod);
    }

    /**
     * @param ResolverMethod $resolverMethod
     * @return ResolverStep
     */
    protected function generateResolverStep(ResolverMethod $resolverMethod)
    {
        $nextResolveStep = new ResolverStep();
        $nextResolveStep->setResolverMethod($resolverMethod);
        $nextResolveStep->resolve();
        return $nextResolveStep;
    }

    protected function nextResolveSteps()
    {
        if ($this->resolved) {
            throw new \LogicException('Grid already resolved');
        }
        $chains = array();
        foreach ($this->chains as $resolverChain) {
            $grid = $resolverChain->getCurrentGrid();
            try {
                $resolveMethod = $this->foundNextResolveGridMethod($grid);
            }
            catch (InvalidGridException $e) {
                continue;
            }

            if ($resolveMethod === null) {
                if (!$grid->getEmptyCaseCount()) {
                    continue;
                }

                $secondResolverChain = clone $resolverChain;

                $nextResolverStep = $this->generateTryResolverStep($grid, Grid::ZERO);
                $resolverChain->addResolverStep($nextResolverStep);
                $chains[] = $resolverChain;

                $nextResolverStep = $this->generateTryResolverStep($grid, Grid::ONE);
                $secondResolverChain->addResolverStep($nextResolverStep);
                $chains[] = $secondResolverChain;
                continue;
            }

            $nextResolverStep = $this->generateResolverStep($resolveMethod);
            $resolverChain->addResolverStep($nextResolverStep);

            $resolvedGrid = $nextResolverStep->getResolvedGrid();
            if ($grid->getGridString() === $resolvedGrid->getGridString()) {
                throw new \LogicException('No change ?');
            }
            if ($resolvedGrid->getChecker()->hasResolved()) {
                $this->resolved = true;
                $this->resolvedGrid = $resolvedGrid;
                $this->resolvedChain = $resolverChain;
                $chains = array();
                break;
            }
            $chains[] = $resolverChain;
        }
        $this->chains = $chains;
    }

    /**
     * @return ResolverChain
     */
    public function getResolvedChain()
    {
        return $this->resolvedChain;
    }

    public function resolve()
    {
        do {
            $this->nextResolveSteps();
        }
        while ($this->chains);

        if (!$this->resolved) {
            throw new InvalidGridException('Not resovled grid');
        }
    }
}