/**
  * should:
  *      initiate game for player and opponent(s) with specific size
  *      each battlefield should have ships
  *      should have at least one AI controlled opponent
  *
  * @see GameBuilder::buildGame
  * @test
  */
 public function buildGame()
 {
     $request = new GameInitiationRequest($this->getSharedFixtureContent('game-initiation-requests/valid/2-players-7x7.json'));
     $game = static::$gameBuilder->buildGame($request);
     $this->assertCount(2, $game->getBattlefields());
     foreach ($game->getBattlefields() as $battlefield) {
         $this->assertCount(49, $battlefield->getCells());
         $this->assertTrue(BattlefieldModel::hasUnfinishedShips($battlefield));
     }
 }
 /**
  * @param Battlefield $battlefield
  *
  * @return Cell
  * @throws AIException
  * @throws CellException
  */
 public function processCPUTurn(Battlefield $battlefield) : Cell
 {
     $cells = $this->strategyService->chooseCells($battlefield);
     try {
         return self::pickCellToAttack($cells);
     } catch (CellException $e) {
         $cells = BattlefieldModel::getLiveCells($battlefield);
         return self::pickCellToAttack($cells);
     }
 }
 /**
  * @see BattlefieldModel::flagWaterAroundShip
  * @test
  */
 public function flagWaterAroundShipOnA1AlreadyDead()
 {
     $battlefield = MockFactory::getBattlefieldMock();
     $battlefield->getCellByCoordinate('A1')->setFlags(CellModel::FLAG_DEAD);
     $cell = $battlefield->getCellByCoordinate('B2')->setFlags(CellModel::FLAG_SHIP);
     BattlefieldModel::flagWaterAroundShip($cell);
     foreach (['A2', 'A3', 'B1', 'B3', 'C1', 'C2', 'C3'] as $coordinate) {
         $this->assertTrue($battlefield->getCellByCoordinate($coordinate)->hasFlag(CellModel::FLAG_SKIP));
     }
 }
 public function buildGame(GameInitiationRequest $request) : Game
 {
     $game = new Game();
     $this->attachAIBattlefields($game, $request->getOpponents(), $request->getSize());
     $player = $this->playerModel->createOnRequestHumanControlled($request->getPlayerName());
     $battlefield = BattlefieldModel::generate($request->getSize(), $request->getCoordinates());
     $battlefield->setPlayer($player);
     $game->addBattlefield($battlefield);
     /** for test purposes only - mark player cells as damaged */
     $battlefield->getCellByCoordinate('A2')->setFlags(CellModel::FLAG_DEAD_SHIP);
     $battlefield->getCellByCoordinate('A1')->setFlags(CellModel::FLAG_DEAD_SHIP);
     return $game;
 }
 public static function getBattlefieldMock(int $size = 7) : Battlefield
 {
     $battlefield = BattlefieldModel::generate($size)->setPlayer(static::getPlayerMock(''));
     return $battlefield;
 }
        });
        it('should return 48 cells from 7x7 battlefield 48 of them not flagged with CellModel::FLAG_DEAD', function () {
            $this->battlefield->getCellByCoordinate('A1')->addFlag(CellModel::FLAG_DEAD);
            $cells = BattlefieldModel::getLiveCells($this->battlefield);
            expect($cells)->toBeA('array')->toHaveLength(48);
            $this->iterateRecievedCells($cells);
        });
        it('should return empty array from 7x7 battlefield all of them flagged with CellModel::FLAG_DEAD', function () {
            /** @var Cell $cell */
            foreach ($this->battlefield->getCells() as $cell) {
                $cell->addFlag(CellModel::FLAG_DEAD);
            }
            $cells = BattlefieldModel::getLiveCells($this->battlefield);
            expect($cells)->toBeA('array')->toHaveLength(0);
        });
    });
    /**
     * @see BattlefieldModel::hasUnfinishedShips
     */
    describe('::hasUnfinishedShips - should return true if Battlefield contains cells flagged with CellModel::FLAG_SHIP and not flagged with CellModel::FLAG_DEAD', function () {
        it('should return false', function () {
            $battlefield = MockFactory::getBattlefieldMock();
            expect(BattlefieldModel::hasUnfinishedShips($battlefield))->toBe(false);
        });
        it('should return true', function () {
            $battlefield = MockFactory::getBattlefieldMock();
            $battlefield->getCellByCoordinate('A1')->addFlag(CellModel::FLAG_SHIP);
            expect(BattlefieldModel::hasUnfinishedShips($battlefield))->toBe(true);
        });
    });
});
 /**
  * invoke game processing method on Unfinished Game
  *
  * @see     GameProcessor::processTurn
  * @test
  *
  * @depends processTurnOnFinishedGame
  */
 public function processTurnToNotWin()
 {
     $game = MockFactory::getGameMock();
     $aiBattlefield = $game->getBattlefields()[0];
     $aiBattlefield->setPlayer(MockFactory::getAIPlayerMock(''));
     foreach ($game->getBattlefields() as $battlefield) {
         $battlefield->getCellByCoordinate('A1')->addFlag(CellModel::FLAG_SHIP);
         $battlefield->getCellByCoordinate('A2')->addFlag(CellModel::FLAG_SHIP);
     }
     $game = static::$gameProcessor->processTurn($aiBattlefield->getCellByCoordinate('A1'));
     foreach ($game->getBattlefields() as $battlefield) {
         $this->assertCount(48, BattlefieldModel::getLiveCells($battlefield));
         /** as one cell should be dead */
         $this->assertTrue(BattlefieldModel::hasUnfinishedShips($battlefield));
     }
     $this->assertTrue($aiBattlefield->getCellByCoordinate('A1')->hasFlag(CellModel::FLAG_DEAD_SHIP));
     $this->assertNull($game->getResult());
 }
 /**
  * @since 21.0
  *
  * @param Battlefield $battlefield
  * @param Player      $attacker
  * @param Cell        $cell
  *
  * @return bool - true if target battlefield do not have any life ships, otherwise false
  * @throws GameProcessorException
  */
 protected function processPlayerTurnOnBattlefield(Battlefield $battlefield, Player $attacker, Cell $cell) : bool
 {
     /** do not process turn on itself */
     if ($battlefield->getPlayer() === $attacker) {
         throw new GameProcessorException('player attacked itself');
     }
     $cell = $this->processPlayerTurn($battlefield, $cell);
     if (CellModel::isShipDead($cell)) {
         BattlefieldModel::flagWaterAroundShip($cell);
         return !BattlefieldModel::hasUnfinishedShips($battlefield);
     }
     return false;
 }