davaxi/Takuzu

View on GitHub
src/GridChecker.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace Davaxi\Takuzu;

/**
 * Class GridChecker
 * @package Davaxi\Takuzu
 */
class GridChecker
{
    const CONSECUTIVE_LIMIT = 2;

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

    /**
     * @var bool
     */
    protected $resolved = false;

    /**
     * Checker constructor.
     * @param Grid $grid
     */
    public function __construct(Grid $grid)
    {
        $this->grid = &$grid;
    }

    /**
     * Compute resolved flag
     */
    public function checkHasResolved()
    {
        $this->resolved = false;
        if ($this->grid->getEmptyCaseCount()) {
            return;
        }
        $lines = $this->grid->getLines();
        if (!$this->checkLines($lines)) {
            return;
        }
        $columns = $this->grid->getColumns();
        if (!$this->checkLines($columns)) {
            return;
        }
        $this->resolved = true;
        return;
    }

    /**
     * @param integer $width
     * @param integer $height
     */
    public function checkSides($width, $height)
    {
        $this->checkSideLength('width', $width);
        $this->checkSideLength('height', $height);
    }

    /**
     * @param integer $lineNo
     * @param integer $columnNo
     * @throws InvalidGridException
     */
    public function checkGridPosition($lineNo, $columnNo)
    {
        if ($lineNo < 0 || $lineNo >= $this->grid->getHeight()) {
            throw new InvalidGridException('Expected line: 0 to ' . $this->grid->getHeight());
        }
        if ($columnNo < 0 || $columnNo >= $this->grid->getWidth()) {
            throw new InvalidGridException('Expected column: 0 to ' . $this->grid->getWidth());
        }
    }

    /**
     * @param $value
     */
    public function checkGridValue($value)
    {
        $allowedValues = array(
            Grid::UNDEFINED,
            Grid::ZERO,
            Grid::ONE,
        );
        if (!in_array($value, $allowedValues, true)) {
            throw new \InvalidArgumentException('Expected value: ' . implode(', ', $allowedValues));
        }
    }

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

    /**
     * @param array $lines
     * @return bool
     */
    protected function checkLines(array &$lines)
    {
        foreach ($lines as $line) {
            if (!$this->checkLineValuesDefined($line)) {
                return false;
            }
            if (!$this->checkLineValuesCollision($line)) {
                return false;
            }
            if (!$this->checkLineValuesEqualities($line)) {
                return false;
            }
        }
        if (!$this->checkLinesDistinctSeries($lines)) {
            return false;
        }
        return true;
    }

    /**
     * @param array $line
     * @return bool
     */
    protected function checkLineValuesDefined(array $line)
    {
        foreach ($line as $value) {
            if ($value === Grid::UNDEFINED) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param array $line
     * @return boolean
     */
    protected function checkLineValuesCollision(array $line)
    {
        $previousValue = null;
        $previousValueCount = 1;
        foreach ($line as $value) {
            if ($previousValue !== $value) {
                $previousValue = $value;
                $previousValueCount = 1;
                continue;
            }
            $previousValueCount++;
            if ($previousValueCount > static::CONSECUTIVE_LIMIT) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param array $line
     * @return bool
     */
    protected function checkLineValuesEqualities(array $line)
    {
        $equality = 0;
        foreach ($line as $value) {
            if ($value === Grid::ZERO) {
                $equality++;
                continue;
            }
            if ($value === Grid::ONE) {
                $equality--;
                continue;
            }
        }
        return $equality === 0;
    }

    /**
     * @param array $lines
     * @return bool
     */
    protected function checkLinesDistinctSeries(array $lines)
    {
        $convertedLines = array_map(function(array $line) {
            $line = array_map('\Davaxi\Takuzu\GridHelpers::getValueLabel', $line);
            return implode($line);
        }, $lines);
        return array_unique($convertedLines) === $convertedLines;
    }

    /**
     * @param string $side
     * @param integer $length
     * @throws InvalidGridException
     */
    protected function checkSideLength($side, $length)
    {
        if (!is_numeric($length)) {
            throw new InvalidGridException('Invalid ' . $side . ' format');
        }
        $length = (int)$length;
        if ($length <= 0) {
            throw new InvalidGridException('Invalid ' . $side . ' value');
        }
        if ($length % 2 !== 0) {
            throw new InvalidGridException('Invalid ' . $side . ' value');
        }
    }
}