<?php
declare (strict_types=1);
final class Position
{
private $row;
private $column;
public function __construct(int $row, int $column)
{
$this->row = $row;
$this->column = $column;
}
public function is(Position $position) : bool
{
return $this->row === $position->row && $this->column === $position->column;
}
}
final class Tile
{
private $position;
private $clicked;
public function __construct(Position $position)
{
$this->position = $position;
$this->clicked = false;
}
public static function clicked(Position $position) : self
{
$tile = new self($position);
$tile->clicked = true;
return $tile;
}
public function position() : Position
{
return $this->position;
}
public function click() : self
{
if ($this->isClicked()) {
throw new \RuntimeException('Tile can\'t be clicked twice');
}
return self::clicked($this->position);
}
public function isClicked() : bool
{
return $this->clicked;
}
}
final class Tiles
{
/**
* @var Tile[]
*/
private $tiles;
public function __construct(Tile ...$tiles)
{
$this->tiles = $tiles;
}
public function count() : int
{
return \count($this->tiles);
}
public function hasMoreThan(Position $position, int $expectedLimit) : bool
{
return $this->find(
function(Tile $tile) use ($position) {
return $tile->position()->is($position);
}
)->count() > $expectedLimit;
}
public function each(callable $callback) : void
{
array_map($callback, $this->tiles);
}
public function find(callable $callback) : Tiles
{
return new Tiles(...array_filter(
$this->tiles,
$callback
));
}
public function map(callable $callback) : array
{
return array_map($callback, $this->tiles);
}
public function has(Position $position) : bool
{
return (bool) $this->find(
function(Tile $tile) use ($position) {
return $tile->position()->is($position);
}
)->count();
}
public function clickedTiles() : int
{
return (int) array_reduce(
$this->tiles,
function(int $clicked, Tile $nextTile) {
return $nextTile->isClicked() ? $clicked + 1 : $clicked;
},
0
);
}
}
final class Board
{
/**
* @var Tiles
*/
private $tiles;
public function __construct(Tiles $tiles)
{
$tiles->each(function(Tile $tile) use ($tiles) {
if ($tiles->hasMoreThan($tile->position(), 1)) {
throw new \RuntimeException('Board can have only tile at each position');
}
});
$this->tiles = $tiles;
}
public function click(Position $position) : void
{
if (!$this->tiles->has($position)) {
throw new \RuntimeException('Tile does not exists');
}
$this->tiles = new Tiles(...$this->tiles->map(function(Tile $tile) use ($position) {
return $tile->position()->is($position)
? $tile->click()
: $tile;
}));
}
public function score() : int
{
return $this->tiles->clickedTiles();
}
}
$board = new Board(new Tiles(
new Tile(new Position(0, 0)),
new Tile(new Position(0, 1))
));
$board->click(new Position(0, 0));
$board->click(new Position(0, 1));
var_dump($board->score());