<?php
abstract class Animal
{
    protected $x;
    protected $y;
    protected $icon;
    protected $field;
    
    public function __construct($coordinateX, $coordinateY, $symbol)
    {
        $this->x    = $coordinateX;
        $this->y    = $coordinateY;
        $this->icon = $symbol;
    }
    public function getCoordinateX()
    {
        return $this->x;
    }
    public function getCoordinateY()
    {
        return $this->y;
    }
    
    public function showCoordinates()
    {
        echo "Координата X =  [$this->x] ";
        echo "Координата Y = [$this->y]";
    }
    
    
    public function getIcon()
    {
        return $this->icon;
    }
    
    public function moveX($x)
    {
        if (($this->field->isXOnMap($this->x + $x)) && !$this->field->checkTile($this->x + $x, $this->y)) {
            $this->x = $this->x + $x;
        }
    }
    
    public function moveY($y)
    {
        if (($this->field->isYOnMap($this->y + $y)) && !$this->field->checkTile($this->x, $this->y + $y)) {
            $this->y = $this->y + $y;
        }
        
    }
    
    
    public function setField(GameField $field)
    {
        $this->field = $field;
    }
    
    public function unsetField()
    {
        $this->field = null;
    }
}

class Mouse extends Animal
{
    
    
    public function findAllCats()
    {
        $cats    = array();
        $animals = $this->field->shareAnimals();
        foreach ($animals as $animal) {
            if ($animal instanceof Cat && (abs($this->x - $animal->getCoordinateX()) <= 9 || abs($this->y - $animal->getCoordinateY()) <= 9)) {
                $cats[] = $animal;
            }
        }
        return $cats;
    }
    
    
    
    public function defineAllAvaibleTurns()
    {
        $ways = array(
            'up' => array(
                'x' => 1,
                'y' => 0,
                'score' => INF
            ),
            'down' => array(
                'x' => -1,
                'y' => 0,
                'score' => INF
            ),
            'left' => array(
                'x' => 0,
                'y' => -1,
                'score' => INF
            ),
            'right' => array(
                'x' => 0,
                'y' => 1,
                'score' => INF
            )
            
            
            
        );
        $list = array(
            'stand' => array(
                'x' => 0,
                'y' => 0,
                'score' => 1
            )
        );
        
        foreach ($ways as $key => $value) {
            
            if ($this->field->isXOnMap($this->x + $value['x']) && $this->field->isYOnMap($this->y + $value['y']) && !$this->field->checkTile($this->x + $value['x'], $this->y + $value['y'])) {
                $list[$key] = $value;
                
                
            }
        }
        
        return $list;
        
    }
    
    public function calculateScore($list)
    {
        $cats = $this->findAllCats();
        
        
        
        
        $previousUp    = INF;
        $previousDown  = INF;
        $previousRight = INF;
        $previousLeft  = INF;
        foreach ($cats as $cat) {
            $differenceX = $this->x - $cat->getCoordinateX();
            $differenceY = $this->y - $cat->getCoordinateY();
            
            
            if (abs($differenceX) > abs($differenceY) && $differenceX < 0 && abs($differenceX) < $previousUp && array_key_exists('up', $list)) {
                $list['up']['score'] = abs($differenceX);
                $previousUp          = abs($differenceX);
                
            } elseif (abs($differenceX) > abs($differenceY) && $differenceX > 0 && abs($differenceX) < $previousDown && array_key_exists('down', $list)) {
                $list['down']['score'] = abs($differenceX);
                $previousDown          = abs($differenceX);
            } elseif (abs($differenceX) < abs($differenceY) && $differenceY < 0 && abs($differenceY) < $previousRight && array_key_exists('right', $list)) {
                $list['right']['score'] = abs($differenceY);
                $previousRight          = abs($differenceY);
                
            } elseif (abs($differenceX) < abs($differenceY) && $differenceY > 0 && abs($differenceY) < $previousLeft && array_key_exists('left', $list)) {
                $list['left']['score'] = abs($differenceY);
                $previousLeft          = abs($differenceY);
            }
            
            
        }
        
        
        return $list;
    }
    
    public function chooseBestTurn()
    {
        $list     = $this->calculateScore($this->defineAllAvaibleTurns());
        $turn     = 0;
        $previous = 0;
        foreach ($list as $key => $value) {
            if ($value['score'] > $previous) {
                $turn = $value;
            }
        }
        
        
        return $turn;
        
    }
    
    public function makeMove()
    {
        
        
        $turn = $this->chooseBestTurn();
        
        $this->moveX($turn['x']);
        $this->moveY($turn['y']);
        
        
        
    }
    
    
    
}






class Cat extends Animal
{
    protected $sleepCount = 0;
    protected $moveCount = 0;
    
    
    public function getIcon()
    {
        if ($this->sleepCount == 0) {
            return parent::getIcon();
        } else {
            return "@";
        }
    }
    
    
    
    
    public function fallAsleep()
    {
        $this->sleepCount = 1;
        
        $this->moveCount = 0;
    }
    
    public function move($x, $y)
    {
        
        if ($this->sleepCount == 0) {
            
            
            if (!($this->field->checkTile($this->x + $x, $this->y + $y) instanceof Cat)) {
                
                $this->x = $this->x + $x;
                $this->y = $this->y + $y;
                $this->moveCount++;
            }
            
            
            
            if ($this->moveCount == 8) {
                $this->fallAsleep();
                
            }
            $tile = $this->field->checkTile($this->x, $this->y);
            
            if ($tile instanceof Mouse) {
                $this->field->killMouse($tile->getCoordinateX(), $tile->getCoordinateY());
                $this->fallAsleep();
                
            }
        }
        
        
        else {
            $this->sleepCount--;
            
        }
        
    }
    
    public function makeMove()
    {
        $x      = 0;
        $y      = 0;
        $target = $this->findClosestMouse();
        if ($target) {
            if ($target->getCoordinateX() > $this->x) {
                $x = 1;
            } elseif ($target->getCoordinateX() < $this->x) {
                $x = -1;
            }
            
            if ($target->getCoordinateY() > $this->y) {
                $y = 1;
            } elseif ($target->getCoordinateY() < $this->y) {
                $y = -1;
            }
            
            $this->move($x, $y);
        }
    }
    
    
    public function findClosestMouse()
    {
        $previous = INF;
        $target   = 0;
        foreach ($this->field->shareAnimals() as $animal) {
            if (!($animal instanceof Mouse)) {
                continue;
            }
            $distance = abs($this->x - $animal->getCoordinateX()) + abs($this->y - $animal->getCoordinateY());
            if ($distance < $previous) {
                $target   = $animal;
                $previous = $distance;
            }
            
        }
        if ($target) {
            return $target;
        }
    }
    
    
}


class GameField
{
    protected $field;
    protected $animals = array();
    protected $height;
    protected $width;
    
    public function __construct($height, $width)
    {
        $this->height = $height;
        $this->width  = $width;
    }
    public function printField()
    {
        $this->field = array_fill(0, $this->height, array_fill(0, $this->width, " . "));
        
        foreach ($this->animals as $animal) {
            $x                   = $animal->getCoordinateX();
            $y                   = $animal->getCoordinateY();
            $this->field[$x][$y] = $animal->getIcon();
        }
        
        foreach ($this->field as $value) {
            foreach ($value as $dot) {
                echo $dot;
            }
            echo "\n";
        }
    }
    
    public function acquireAnimal(Animal $animal)
    {
        $this->animals[] = $animal;
        $animal->setField($this);
    }
    
    public function shareAnimals()
    {
        return $this->animals;
    }
    
    public function shareHeight()
    {
        return $this->height - 1;
    }
    
    public function shareWidth()
    {
        return $this->width - 1;
    }
    
    public function killMouse($x, $y)
    {
        
        for ($i = 0; $i < count($this->animals); $i++) {
            
            if ($this->animals[$i] instanceof Mouse && $this->animals[$i]->getCoordinateX() == $x && $this->animals[$i]->getCoordinateY() == $y) {
                $this->animals[$i]->unsetField();
                unset($this->animals[$i]);
            }
        }
        
    }
    
    public function checkTile($x, $y)
    {
        
        foreach ($this->animals as $animal) {
            if ($animal->getCoordinateX() == $x && $animal->getCoordinateY() == $y) {
                return $animal;
            }
            
        }
        return false;
    }
    
    public function isXOnMap($x)
    {
        if ($x < 0 || $x > ($this->height - 1)) {
            return false;
        } else {
            return true;
        }
    }
    
    public function isYOnMap($y)
    {
        if ($y < 0 || $y > ($this->width - 1)) {
            return false;
        } else {
            return true;
        }
    }
    
    public function testTheKitty()
    {
        
        foreach ($this->animals as $animal) {
            if ($animal instanceof Cat) {
                $animal->makeMove();
                
                
                
            }
            
            
        }
        foreach ($this->animals as $animal) {
            if ($animal instanceof Mouse) {
                $animal->makeMove();
                
                
            }
            
        }
        
        
        
        
    }
}


$test   = new GameField(7, 7);
$mouse1 = new Mouse(3, 1, "1");
$mouse2 = new Mouse(5, 2, "2");
$kitty  = new Cat(6, 6, "K");
$kitty2 = new Cat(5, 5, "k");

$test->acquireAnimal($mouse1);
$test->acquireAnimal($mouse2);
$test->acquireAnimal($kitty);
$test->acquireAnimal($kitty2);
$test->printField();
echo "\n";
for ($i = 0; $i < 40; $i++) {
    $test->testTheKitty();
    $test->printField();
    echo "\n";
    
    
}