<?php
header("Content-Type: text/plain");
mb_internal_encoding('utf-8');
//ini_set('html_errors', false);
//xdebug_disable();

//  ООП. Кошки - мышки

// Константы
const cat = 'Cat';
const mouse = 'Mouse';
const dog = 'Dog';

// Класс Животного (игрока)
abstract class Animal
{
    // Id
    private $id;
    
    // Счетчик экземпляров класса
    protected static $counter = 1;
        
    // Координаты ХY
    protected $xy = array();
    
    // Кол-во пропускаемых ходов
    protected $restingTime = 0;
    
    // Hp
    protected $hp = 100;
    
    // Генератор животного
    public function __construct($x, $y)
    {
        $this->xy['x'] = $x;
        $this->xy['y'] = $y;
        $this->id = self::$counter++; 
    }
    public function getId()
    {
        return $this->id;
    }
    
    // Выбор наилучшего ВариантаХода (большего веса)
    protected function chooseBestMoveVariant($moveVariants)
    {        
        // Сортировка ВариантовХода по убыванию веса
        usort ($moveVariants,
            function ($moveVariantA, $moveVariantB ) {
                $weightA = $moveVariantA->getWeight();
                $weightB = $moveVariantB->getWeight();
                if ($weightA==$weightB) return 0;
                return ($weightA<$weightB) ? 1 : -1;                
            });
        // Выбираем ходы с наибольшим весом
        $bestMoveVariantWeight = $moveVariants[0]->getWeight();
        $bestMoveVariants = array_filter ($moveVariants,
            function ($moveVariant) use ($bestMoveVariantWeight) {
                return ($bestMoveVariantWeight == $moveVariant->getWeight());
            });
        $bestMoveVariantKey = array_rand($bestMoveVariants, 1);
        return $bestMoveVariants[$bestMoveVariantKey];
    }
    
    // Возвращает координаты
    public function getXY()
    {
        return $this->xy;
    }
    
    // Установка координат
    public function setXY($xy)
    {
        $this->xy['x'] = $xy['x'];
        $this->xy['y'] = $xy['y'];
    }
    
    // Возвращает время отдыха
    public function getRestingTime()
    {
        return $this->restingTime;
    }
    
    public function setRestingTime($moveQuantity)
    {
        $this->restingTime = $moveQuantity;
    }
    
    public function getHP()
    {
        return $this->hp;
    }
    
    // Находим животных в зоне видимости
    protected function getScope($animals)
    {
        $xy = $this->xy;
        $visionLimits = $this->getVisionLimits();
        $scope = array_filter($animals, function ($animal) use ($xy, $visionLimits) {
            $animalXY = $animal->getXY();
            return (abs($animalXY['x'] - $xy['x']) <= $visionLimits &&
                    abs($animalXY['y'] - $xy['y']) <= $visionLimits);
        });
        return $scope;
    }
    // Находим определенных животных
    protected function getAnimal($animals, $animalType)
    {
        $scope = array_filter($animals, function ($animal) use ($animalType) {
            return (get_class($animal) == $animalType);
        });
        return $scope;
    }
    // Определяет направление
    // Знак "+" значения косинуса указывает направление в сторону цели, "-" от нее
    protected function getDirections($moveVariant, $target)
    {
        $moveVariantVector = new Vector($this->xy, $moveVariant->getXY());
        $targetsVector = new Vector($this->xy, $target->getXY());
        $cos = round($targetsVector->getCos($moveVariantVector, $targetsVector));
        return $cos;
    }
    protected function getDistance($target)
    {
        $target = $target;
        $targetsVector = new Vector($this->xy, $target->getXY());
        return $targetsVector->getLenght();
    }

    // Присваивает вес вариантам хода на основе
    // совпадения напрвления до целей и растоянию до них умноженный на коэфициент
    protected function getWeightsByDirections($targets, $moveVariants, $k = 1)
    {
        // Если целей нет
        if (count($targets)==0) return;
        
        foreach ($moveVariants as $moveVariant) {
            foreach ($targets as $target) {
                // Вес хода прямо пропорционален напрвлению к цели
                // и обратно пропорционален растоянию до цели
                $distance = $this->getDistance($target);
                $direction = $this->getDirections($moveVariant, $target); 
                // Исключаем варианты когда длина или кос не определены
                if ($distance==0 && $direction==null) {
                    continue;
                } else {
                    // Вычисляем вес умноженный на коэфициент
                    $weight = $moveVariant->getWeight() + ($k*$direction/pow($distance,2));
                    // Присваиваем вес хода
                    $moveVariant->setWeight($weight);
                }                 
            }
        }      
    }
    // Исключение ходов занятых оживотными
    protected function removeMoveByAnimals($animals, $moveVariants)
    {
        foreach ($moveVariants as $key => $moveVariant) {
            $moveVariantXY=$moveVariant->getXY();
            foreach ($animals as $animal) {
                $animalXY=$animal->getXY();
                // Исключаем из проверки ход на котором стоим
                if ($animalXY['x'] == $this->xy['x'] &&
                    $animalXY['y'] == $this->xy['y']) continue;
                // Проверяем остальные
                if ($moveVariantXY['x']==$animalXY['x'] &&
                    $moveVariantXY['y']==$animalXY['y']) {
                    unset ($moveVariants[$key]);
                    
                }
            }
        }
        $moveVariants = $moveVariants;
        return $moveVariants;
    }
    // Определение углов в поле видимости
    protected function getCorners ()
    {
        // Определяем координаты углов
        $fieldSizeX = fieldSize;
        $fieldSizeY = fieldSize;
        // Создаем углы координаты углов
        $corner['x']=fieldSize;
        
        // Оставляем углы которые видим
        // Возвращаем их
    }
    // Убийство
    public function toDie()
    {
        $this->hp = 0;
        return true;
    }
    
    // Игровой ход
    abstract function move($animals, $moveVariants, $corners);
      
    // Скорость перемещения (клеток за ход)
    abstract protected function getSpeed();
    
    // Возможность ходить по горизонт, вертикали, диагонали
    //abstract function getMoveAbility();
    
    // Область видимости вокруг себя
    abstract protected function getVisionLimits();
    
    // Оценка ходов
    abstract protected function setRating($animals, $moveVariants, $corners);
    
    // Исключение занятых ходов
    abstract protected function removeOccupiedMove($animals, $moveVariants); 
}

class Mouse extends Animal
{    
    // Неуязвимость
    private $megaMouse = false;
    
    // Скорость перемещения (клеток за ход)
    public function getSpeed()
    {
        return 1;
    }    
    // Область видимости вокруг себя
    public function getVisionLimits()
    {
        return 4;
    }
    // Игровой ход
    public function move($animals, $moveVariants, $corners)
    {
        // Исключение занятых ходов
        $moveVariants = $this->removeOccupiedMove($animals, $moveVariants);
        // Исключения вариантов хода по вертикали
        $moveVariants = $this->removeDiagonalMove($moveVariants);
        // Оценка ходов
        $this->setRating($animals, $moveVariants, $corners);
        // Выбор наилучшего хода (наибольший вес)
        $bestMoveVariant = $this->chooseBestMoveVariant($moveVariants);
        // Ход
        $this->setXY($bestMoveVariant->getXY());
        // Проверка неуязвимости
        $this->transformMegaMouse($animals);
    }
    
    // Убийство
    public function toDie()
    {
        if (!$this->megaMouse) {
            return parent::toDie();
        } else {
            return false;
        }
    }
    
    // Оценка ходов
    protected function setRating($animals, $moveVariants, $corners)
    { 
        // Область видимости
        $animals = $this->getScope($animals);
        $corners = $this->getScope($corners);
        
        // Находим мышей в поле видимости
        $mouses = ($this->getAnimal($animals, mouse));
        
        // Присвиваем реитинг = 1 вариантам ходов по направлению к мышам
        $this->getWeightsByDirections($mouses, $moveVariants, 2);        
               
        // Находим кошек в поле видимости
        $cats = ($this->getAnimal($animals, cat));
        
        // Присвиваем реитинг = 2 вариантам ходов по направлению к кошкам 
        $this->getWeightsByDirections($cats, $moveVariants, -10);        
       
        // Присвиваем реитинг = -1 вариантам ходов по направлению к углам
        $this->getWeightsByDirections($corners, $moveVariants, -1);
        
        // Находим собак в поле видимости
        $dog = ($this->getAnimal($animals, dog));
        // Если на соседней клетке есть собака, то такой ход +10 
        
        // Присвиваем реитинг = -1 вариантам ходов по направлению к углам
        $this->getWeightsByDirections($dog, $moveVariants, 1);
    }
    
    // Исключение занятых ходов
    protected function removeOccupiedMove($animals, $moveVariants)
    {  
        $moveVariants = $this->removeMoveByAnimals($animals, $moveVariants);
        return $moveVariants;
    }
    
    // Метод исключения вариантов хода по вертикали
    private function removeDiagonalMove($moveVariants) {
        
        // У вертикальных ходов координаты X Y равны по модулю, относительно текщего положения животного
        foreach ($moveVariants as $key => $moveVariant) {            
            $moveVariantXY=$moveVariant->getXY();
            // Исключаем из проверки ход на котором стоим
            if ($moveVariantXY['x'] == $this->xy['x'] &&
                $moveVariantXY['y'] == $this->xy['y']) continue;
            // Проверяем остальные
            if (abs($moveVariantXY['x']-$this->xy['x'])==abs($moveVariantXY['y']-$this->xy['y'])) {
                unset($moveVariants[$key]);
            }
        } 
        return $moveVariants;  
    }
    // Трансформирует 3х мышей в мегамышь неуязвимую для кошек
    private function transformMegaMouse ($animals)
    {
        $mouses = $this->cheсkNeighborMouses($animals);
        if ($mouses>=3) {
            $this->megaMouse = true;
        } else {
            $this->megaMouse = false;
        }
    }
    // Считает кол-во мышей в соседних клетках
    private function cheсkNeighborMouses($animals)
    {
        // Извлекаем мышей
        $mouses = $this->getAnimal($animals, mouse);
        // Сравнить координаты текущей мыши с координатами остальных
        // Если модуль разницы координат в пределах 1 то ОК
        $xy = $this->xy;
        $mouses = array_filter($mouses,
                function ($mouse) use ($xy) {
                    $mouseXY = $mouse->getXY();
                    return ((abs($xy['x'] - $mouseXY['x'])<=1) &&
                            (abs($xy['y'] - $mouseXY['y'])<=1));
                         });
        return count($mouses);        
    } 
}
class Cat extends Animal
{    
    // Счетчик ходов
    private $moveCounter = 0;
    
    // Скорость перемещения (клеток за ход)
    public function getSpeed()
    {
        return 1;
    }
    
    // Возможность ходить по горизонт, вертикали, диагонали
    public function getMoveAbility()
    {
        return array(true, true, true);
    }
    // Область видимости вокруг себя
    public function getVisionLimits()
    {
        return INF;
    }    
    
    // Игровой ход
    public function move($animals, $moveVariants, $corners)
    {
        // Проверка кол-ва пропускаемых ходов
        if ($this->getRestingTime()>0 ) {
            // Уменьшаем счетчик пропускаемых ходов
            $this->setRestingTime($this->getRestingTime()-1);
            return;
        }
        // Исключение занятых ходов
        $moveVariants = $this->removeOccupiedMove($animals, $moveVariants);
        // Оценка ходов
        $this->setRating($animals, $moveVariants, $corners);
        // Выбор наилучшего хода (наибольший вес)
        $bestMoveVariant = $this->chooseBestMoveVariant($moveVariants);
        // Проверка съедания мыши
        $eatenMouse = $this->checkEatenMouse($animals, $bestMoveVariant);
        if ($eatenMouse) $this->eatMouse($eatenMouse);
        // Ход
        $this->setXY($bestMoveVariant->getXY());
        // Прибавляем счетчик
        $this->moveCounter++;
        if ($this->moveCounter>=8) {
            $this->setRestingTime(1);
            $this->moveCounter=0;
        }
    }
    // Оценка ходов
    protected function getWeightsByDirections($targets, $moveVariants, $coeficient = 1)
    {
        // Если в соседней клетке есть мышь, рейтинг = INF
        foreach ($moveVariants as $moveVariant)
        {           
            foreach ($targets as $mouse) {
                $mouseXY = $mouse->getXY();
                $moveVariantXY = $moveVariant->getXY();              
                
                if ($mouseXY['x']==$moveVariantXY['x'] &&
                    $mouseXY['y']==$moveVariantXY['y']) {
                    $moveVariant->setWeight(INF); //$moveVariant->getWeight() + 10
                }
            }            
        }
        parent::getWeightsByDirections($targets, $moveVariants, $coeficient = 1);
    }
    
    // Оценка ходов
    protected function setRating($animals, $moveVariants, $corners)
    {
        // Область видимости
        $scope = $this->getScope($animals);
        
        // Находим мышей в поле видимости
        $mouses = ($this->getAnimal($animals, mouse));
        
        // Присвиваем реитинг = 1 вариантам ходов по направлению к мышам
        $this->getWeightsByDirections($mouses, $moveVariants, 2);
    }
    // Исключение занятых ходов
    protected function removeOccupiedMove($animals, $moveVariants)
    {
        // Исключаем кошек
        $cats = $this->getAnimal($animals, cat);
        $moveVariants = $this->removeMoveByAnimals($cats, $moveVariants);
        // Исключаем соседние с собакой ходы
        $dogs = $this->getAnimal($animals, dog);        
        $xy = $this->xy;
        /*
        $moveVariants = array_udiff($moveVariants, $dogs,
                            function($move, $dog) use ($xy){
                                $moveXY = $move->getXY();
                                $dogXY = $dog->getXY();
                                $absX = abs($moveXY['x']-$dogXY['x']);
                                $absY = abs($moveXY['y']-$dogXY['y']);       
                                if ($moveXY['x']-$dogXY['x'] && $moveXY['y']==$xy['y']) {
                                    return 1;
                                }
                            });
        */
        foreach ($moveVariants as $moveVariant) {            
            foreach($dogs as $dog) {
                $moveXY = $moveVariant->getXY();
                $dogXY = $dog->getXY();
                $xy = $this->xy;
                $absX = abs($moveXY['x']-$dogXY['x']);
                $absY = abs($moveXY['y']-$dogXY['y']);  
                if (($moveXY['x']==$xy['x'] && $moveXY['y']==$xy['y']) |
                    ($absX>1 && $absY>1)) {
                    $resultMoves[]=$moveVariant;
                }
            }
        }
        $moveVariants = $this->removeMoveByAnimals($dogs, $moveVariants);
        // Исключаем cобак
        return $resultMoves;
    }
    // Проверка съедания мыши
    private function checkEatenMouse($animals, $bestMoveVariant)
    {
        $moveVariantXY=$bestMoveVariant->getXY();        
        $mouses = $this->getAnimal($animals, mouse);          
        foreach ($mouses as $mouse) {
            $mouseXY = $mouse->getXY();
            if ($mouseXY['x'] == $moveVariantXY['x'] &&
                $mouseXY['y'] == $moveVariantXY['y']) {
                return $mouse;
            }
        }
    }    
    // Поедание мыши
    private function eatMouse ($mouse)
    {
        $isDead = $mouse->toDie();
        if ($isDead) $this->setRestingTime(1);        
    }    
}
class Dog extends Animal
{
    // Скорость перемещения (клеток за ход)
    public function getSpeed()
    {
        return 2;
    }
    
    // Возможность ходить по горизонт, вертикали, диагонали
    public function getMoveAbility()
    {
        return array(true, true, true);
    }
    
    // Область видимости вокруг себя
    public function getVisionLimits()
    {
        return INF;
    }    
    
    // Игровой ход
    public function move($animals, $moveVariants, $corners)
    {
        // Метод исключения ходов соседних к текущему положению
         $moveVariants = $this->exlusionNeighborMove($moveVariants);
        // Исключение занятых ходов
        $moveVariants = $this->removeOccupiedMove($animals, $moveVariants);
        // Оценка ходов
        $this->setRating($animals, $moveVariants, $corners);
        // Выбор наилучшего хода (наибольший вес)
        $bestMoveVariant = $this->chooseBestMoveVariant($moveVariants);
        // Ход
        $this->setXY($bestMoveVariant->getXY());
    }
       
    // Оценка ходов
    protected function setRating($animals, $moveVariants, $corners)
    {
        // Присвиваем случайный рейтинг вариантам ходов
        $moveVariants = array_map(function ($moveVariant) {
                                $moveVariant->setWeight(rand(0,10));},
                                    $moveVariants);
        return $moveVariants;
    }
    // Исключение занятых ходов
    protected function removeOccupiedMove($animals, $moveVariants)
    {
        // Исключаем всех животных
        $moveVariants = $this->removeMoveByAnimals($animals, $moveVariants);
        return $moveVariants;
    }
    // Метод исключения ходов соседних к текущему положению
    private function exlusionNeighborMove($moveVariants)
    {
        $xy = $this->xy;
        $moveVariants = array_filter($moveVariants,
                function ($moveVariant) use ($xy) {
                    $moveVariantXY = $moveVariant->getXY();
                    return ((abs($xy['x'] - $moveVariantXY['x'])>1) |
                            (abs($xy['y'] - $moveVariantXY['y'])>1) |
                            (($xy['x'] == $moveVariantXY['x']) &&
                             ($xy['y'] == $moveVariantXY['y'])));
                         });
        return $moveVariants;     
    }
}
// Класс Животные (Игроки)
class Animals
{
    private $animals = array();
    
    public function __construct ($mousesQuantity, $catsQuantity, $dogsQuantity)
    {
        // Создание мышей
        for ($mousesQuantity; $mousesQuantity > 0; $mousesQuantity--) {
            // Генераруем случайные координаты
            $xy = $this->newCoordinat();
            $this->animals[] = new Mouse($xy['x'], $xy['y']);
        }
        // Создание кошек
        for ($catsQuantity; $catsQuantity > 0; $catsQuantity--) {
            $xy = $this->newCoordinat();
            $this->animals[] = new Cat($xy['x'], $xy['y']);
        }
        // Создание собак
        for ($dogsQuantity; $dogsQuantity > 0; $dogsQuantity--) {
            $xy = $this->newCoordinat();
            $this->animals[] = new Dog($xy['x'], $xy['y']);
        }
    }
    
    // Запуск цикла ходов для всех животных
    public function move($corners)
    {
        foreach ($this->animals as $animal) {
            // Генерация всех возможных ВариантовХода
            $moveVariants = MoveVariant::createMoveVariants($animal);            
            $animal->move($this->animals, $moveVariants, $corners);
        }
        // Чистка убитых животных
        $this->cleanUpDeadAnimals();
    }
    
    // Убираем с поля убитых животных
    private function cleanUpDeadAnimals()
    {
        $liveAnimal = array_filter($this->animals,
                        function ($animal) {
                            return ($animal->getHp()>0);
                        });
        $this->animals = $liveAnimal;
    }
    
    // Функция генерации координат
    private function newCoordinat()
    {
        $xy = array();
        $xy['x'] = rand(1, fieldSize);
        $xy['y'] = rand(1, fieldSize);
        
        return  $xy;
    }
    // Уничтожение животного
    public function killAnimal($killingAnimal)
    {
        foreach ($animals as $key => $animal) {
            if ($animal === $killingAnimal) unset ($animals[$key]);
        }
    }    
    public function getAnimalsAsArray ()
    {
        return $this->animals;
    }
}

// Класс ВариантХода
class MoveVariant
{
    // Координаты
    private $xy = array();
    // Вес
    private $weight=0;
        
    public function __construct ($moveX, $moveY)
    {
        $this->xy['x'] = $moveX;
        $this->xy['y'] = $moveY;
    }
    
    // Возвращает координаты
    public function getXY()
    {
        return $this->xy;
    }
    public function setWeight($weight)
    {
        $this->weight = $weight;
    }
    public function getWeight()
    {
        return $this->weight;
    }
    // Фабрика ВариантовXода
    static public function createMoveVariants ($animal)
    {
        // Создание новых ВариантовХода
        $moveVariants = array();
        $animalXY = $animal->getXY();
        $animalSpeed = $animal->getSpeed();
        
        // Координаты начала (верхний левый угол)
        $animalSpeed = $animal->getSpeed();
        do {
            $start['x'] = $animalXY['x']-$animalSpeed;
            $animalSpeed--;
        } while ($start['x']<=0);
        
        $animalSpeed = $animal->getSpeed();
        do {
            $start['y'] = $animalXY['y']-$animalSpeed;
            $animalSpeed--;
        } while ($start['y']<=0);
        // Координаты конца (нижний правый угол)
        $animalSpeed = $animal->getSpeed();
        do {
            $end['x'] = $animalXY['x']+$animalSpeed;
            $animalSpeed--;
        } while ($end['x']>fieldSize);
        
        $animalSpeed = $animal->getSpeed();
        do {
            $end['y'] = $animalXY['y']+$animalSpeed;
            $animalSpeed--;
        } while ($end['y']>fieldSize);
        
        // Генерация ходов        
        $y = $start['y'];
        while ($y<=$end['y']) {
            $x = $start['x'];
            while ($x<=$end['x']) {
                $moveVariants[] = new MoveVariant($x, $y);    
                $x++;
            }
            $y++;
        }
        return $moveVariants;
    }
}
// Класс Вектор
class Vector
{
   private $xy = array();
   private $vectorNorm; // нормализованный вектор
   private $zero;       // нулевой вектор
   
   // Можно создать задавая координаты x и y,
   // либо двумя точками (массивы содержащие координаты)
   public function __construct($startXY, $endXY)
   {
        
        if ((is_int($startXY) && is_int($endXY)) |
            (is_float($startXY) && is_float($endXY))) {
            $this->xy['x'] = $startXY;
            $this->xy['y'] = $endXY;
        } else if (is_array($startXY) && is_array($endXY)) {
            $this->xy['x'] = $endXY['x'] - $startXY['x'];
            $this->xy['y'] = $endXY['y'] - $startXY['y'];
        } else {
            //throw new Exception ("Аргументы для создания класса Вектор
            //                   должны быть или (int, int) или (array, array)");
        }        
   }   
   public function getXY()
   {
        return $this->xy;
   }
   public function isZero()
   {
        if (!isset($this->zero)) {
            if (round($this->xy['x']) == 0 && round($this->xy['y']) == 0) {
                $this->zero = true;
            } else {
                $this->zero = false;
            }
        }
        return $this->zero;
   }
   public function getLenght()
   {
        $sumQuad = pow($this->xy['x'], 2) + pow($this->xy['y'], 2);
        $sqrt = sqrt($sumQuad);
        return $sqrt; //$sqrt;
   }
   
   public function getAngle($vector1, $vector2)
   {
        $vector1Norm = $vector1->getNormalyze();
        $vector2Norm = $vector2->getNormalyze();
        $multiplyResult = $this->multiply($vector1Norm, $vector2Norm);
        // Проверка на нулевые вектора
        if (($vector1->isZero()) | ($vector2->isZero())) {
            $acos = null;
        } else {
            $acos = acos($multiplyResult);
        }
        return $acos;
   }
   
   public function getNormalyze()
   {
        if (!isset($this->vectorNorm)) {
            // Проверка на нулевой вектор
            $length = $this->getLenght();
            if ($length!=0) {
                $x = $this->xy['x']/$length;
                $y = $this->xy['y']/$length;
            } else {
                $x = 0;
                $y = 0;
            }
            
            $this->vectorNorm = new Vector($x, $y);            
        }        
        return $this->vectorNorm;
   }
   public function getCos($vector1, $vector2)
   {
        $multiplyResult = $this->multiply($vector1, $vector2);
        $multiplyLenghtResult = $vector1->getLenght() * $vector2->getLenght();
        if ($multiplyLenghtResult==0) {
            // Если как минимум один из векторов нулевой, к черту такое сравнение
            $cos = null;
        } else {
            $cos = $multiplyResult / $multiplyLenghtResult;
        }        
        /*$cos = cos($this->getAngle($vector1, $vector2));*/
        return $cos;
   }
   public function multiply($vector1, $vector2)
   {
        $vector1XY = $vector1->getXY();
        $vector2XY = $vector2->getXY();
        $multiplyResult = $vector1XY['x'] * $vector2XY['x'] + $vector1XY['y'] * $vector2XY['y'];
        return $multiplyResult;
   }
}
class Corner
{
    public static function getCorners()
    {     
        for ($y = fieldSize; $y>=0; $y=$y-fieldSize) {
            for ($x = fieldSize; $x>=0; $x=$x-fieldSize) {
                // Создаем новый угол
                $corners[] = new MoveVariant ($x, $y);
            }
        }
        return $corners;  
    }
}
// Класс ГеймСет (Игра)
class GameSet
{
    private $raunds = array();
    private $animals;
    private $corners;
    //private $raundCounter = 0;  // Номер раунда
    
    public function __construct ($mousesQuantity, $catsQuantity, $dogsQuantity)
    {
        $this->animals = new Animals ($mousesQuantity, $catsQuantity, $dogsQuantity);
        $this->corners = Corner::getCorners();
    }
    
    // Новый игровой раунд
    public function newRaund($raundNum)
    {
        // Ход животных
        $this->animals->move($this->corners);
    }
    // Представление раунда в виде псевдографики (массива строк)
    public function getGameRoundAsGraphics()
    {
        $gameField = array(); // поле игры, содержит строки из клеток
        $fieldSize = fieldSize;
        // Массив аватаров Животных
        $avatars = array(0 => 'm', 1 => 'K');
        
        // Формируем поле из точек
        // Повторяем для каждой строки
        for ($fieldSize; $fieldSize>0; $fieldSize--) {
            // Заполняем строку "."
            $str = str_repeat(".", fieldSize);
            // Добавляем строку в массив
            $gameField[] = $str;
        }
        // Получаем всех Жиаотных массивом
        $animals = $this->animals->getAnimalsAsArray();
        
        // Проходим по массиву животных
        foreach ($animals as $key => $animal) {
            // Узнаем координаты каждого персонажа
            $xy = $animal->getXY();
            
            // Заменяем соответствующую точку в соответствующей строке id мышки
            $str = $gameField[$xy['y']-1];
            // Мышь обозначаем носером
            if (get_class($animal)==mouse) {
                $str = substr_replace($str, $animal->getId(), $xy['x']-1, 1);
            } elseif (get_class($animal)==dog) {
                $str = substr_replace($str, "D", $xy['x']-1, 1);
            } else {
                // Кошку буквой "K" или "@" если спит
                if ($animal->getRestingTime()>0) {
                     $str = substr_replace($str, "@", $xy['x']-1, 1); 
                } else {
                    $str = substr_replace($str, "K", $xy['x']-1, 1); 
                }
                
            }
            $gameField[$xy['y']-1] = $str;
        }      
        return $gameField;
    }    
    // Печать раунда
    public function printGameRound($raundNum)
    {
        // Получаем раунд в виде псевдографики (массива строк)
        $printingRaund = $this->getGameRoundAsGraphics();
        // Узнаем че по кошкам (мышам)
        $animals = $this->animals->getAnimalsAsArray();
        $cats = array_filter($animals,
                    function ($animal) {
                        return (get_class($animal) == cat);
                    });
        $mouses = array_filter($animals,
                    function ($animal) {
                        return (get_class($animal) == mouse);
                    });
        $dog = array_filter($animals,
                    function ($animal) {
                        return (get_class($animal) == dog);
                    });
        $dogsNum = count($dog);
        $catsNum = count($cats);
        $mousesNum = count($mouses);
        
        // Выводим на экран массив построчно
        foreach ($printingRaund as $key => $str) {            
            if ($key == 0) {
                echo $str . "    " . "Ход: " . "{$raundNum}\n";
            } else if ($key == 1) {
                echo $str . "    " . "Кошек: " . "$catsNum\n";
            } else if ($key == 2) {
                echo $str . "    " . "Мышек: " . "$mousesNum\n";
            } else if ($key == 2) {
                echo $str . "    " . "Собак: " . "$dogsNum\n";
            } else {
                echo "{$str}\n";
            }            
        }
        echo "\n";
    }   
}  

// Создание новой игры
function createNewGame ()
{
    // Вводные данные для создания новой игры
    $raundsQuantity = 20;
    $mousesQuantity = 4;
    $catsQuantity = 2;
    $dogQuantity = 2;
    $fieldSize = 19;
        
    define('fieldSize', $fieldSize);
    
    // Создание новой игры (раунд: 0)    
    $gameSet= new GameSet($mousesQuantity, $catsQuantity, $dogQuantity);
    // Вывести раунд на печать
    $gameSet->printGameRound(0);
    
    // Цикл раундов (начиная с первого)
    for ($raundNum=1; $raundNum<=$raundsQuantity; $raundNum++) {
        // Новый раунд
        $gameSet->newRaund($raundNum);
        // Вывести раунд на печатьs
        $gameSet->printGameRound($raundNum);;
    }
}
createNewGame();

?>