From 62ac7a27e359e3bfda33be9a3175a858ec21a017 Mon Sep 17 00:00:00 2001 From: Thomas Baker Date: Thu, 12 May 2016 21:42:56 +0200 Subject: [PATCH] Add a second type of player with a different strategy. --- exceptions/IllegalArgumentException.php | 4 ++ line.php | 33 +++++++++++++ player.php | 17 +++++-- players/cautiousplayer.php | 65 +++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 exceptions/IllegalArgumentException.php create mode 100644 players/cautiousplayer.php diff --git a/exceptions/IllegalArgumentException.php b/exceptions/IllegalArgumentException.php new file mode 100644 index 0000000..63bffdb --- /dev/null +++ b/exceptions/IllegalArgumentException.php @@ -0,0 +1,4 @@ +placements); } + public function contains($searchTile) { + foreach ($this->tiles() as $tile) { + if ($tile === $searchTile) { + return true; + } + } + return false; + } + + public function sharedProperty() { + $sharedProperty = null; + foreach (Color::colors() as $color) { + foreach ($this->tiles() as $tile) { + if ($sharedProperty === null || $sharedProperty === $tile->color()) { + $sharedProperty = $tile->color(); + } else { + $sharedProperty = null; + break 2; + } + } + } + foreach (Shape::shapes() as $shape) { + foreach ($this->tiles() as $tile) { + if ($sharedProperty === null || $sharedProperty === $tile->shape()) { + $sharedProperty = $tile->shape(); + } else { + return null; + } + } + } + return $sharedProperty; + } + public function __toString() { $a = []; foreach ($this->placements as $placement) { diff --git a/player.php b/player.php index 8728a22..edb754b 100644 --- a/player.php +++ b/player.php @@ -65,15 +65,16 @@ public function move(Board $board, $bagIsEmpty) { if ($board->isEmpty()) { return $this->startingMove($tiles); } + return $this->chooseMove($tiles, $board, $bagIsEmpty); + } + + protected function chooseMove(array $tiles, Board $board, $bagIsEmpty) { $usefulSets = $this->usefulSets($tiles); list($max, $bestMove) = [0, new Move([])]; foreach ($usefulSets as $usefulSet) { $move = $this->bestMoveWith($usefulSet, $board); if ($move) { - $score = $board->score($move); - if (count($move->placements()) === count($tiles) && $bagIsEmpty) { - $score += Score::FINISHING_BONUS; - } + $score = $this->evaluate($tiles, $move, $board, $bagIsEmpty); if ($score > $max) { $max = $score; $bestMove = $move; @@ -83,6 +84,14 @@ public function move(Board $board, $bagIsEmpty) { return $bestMove; } + protected function evaluate(array $tiles, Move $move, Board $board, $bagIsEmpty) { + $score = $board->score($move); + if (count($move->placements()) === count($tiles) && $bagIsEmpty) { + $score += Score::FINISHING_BONUS; + } + return $score; + } + private function usefulSets(array $tiles) { Assert::type($tiles, Tile); if (array_unique($tiles) !== $tiles) { diff --git a/players/cautiousplayer.php b/players/cautiousplayer.php new file mode 100644 index 0000000..34e01c3 --- /dev/null +++ b/players/cautiousplayer.php @@ -0,0 +1,65 @@ +score($move); + if (count($move->placements()) === count($tiles) && $bagIsEmpty) { + $score += Score::FINISHING_BONUS; + } elseif ($this->enablesQwirkle($move, $board, $bagIsEmpty) && $score < 9) { + $score -= 3.5; + } + return $score; + } + + private function enablesQwirkle(Move $move, Board $board, $bagIsEmpty) { + foreach ($move->lines($board) as $line) { + if ($line->length() === count(Color::colors()) - 1) { + $missingPiece = $this->missingPieces($line)[0]; + if ($this->notAccountedFor($missingPiece, $board)) { + return false; + } + } + } + return false; + } + + private function missingPieces($line) { + if ($line->length() <= 1) { + throw new IllegalArgumentException("Qwirkle this lines is part of is ambiguous ($line)."); + } + $sharedProperty = $line->sharedProperty(); + if ($sharedProperty === null) { + throw new IllegalArgumentException("This line does not have a shared property ($line)."); + } + $missing = []; + if (get_class($sharedProperty) === Shape) { + foreach (Color::colors() as $color) { + $tile = Tile::getTile($color, $sharedProperty); + if (!$line->contains($tile)) { + $missing[] = $tile; + } + } + } elseif (get_class($sharedProperty) === Color) { + foreach (Shape::shapes() as $shape) { + $tile = Tile::getTile($sharedProperty, $shape); + if (!$line->contains($tile)) { + $missing[] = $tile; + } + } + } else { + throw new IllegalArgumentException("Unrecognized property $sharedProperty"); + } + return $missing; + } + + private function notAccountedFor($searchTile, Board $board) { + $tiles = Tile::allTiles(); + foreach ($board->tiles() as $tile) { + if ($tile === $searchTile) { + $pos = array_search($tile, $tiles); + unset($tiles[$pos]); + } + } + return in_array($tile, $tiles); + } +}