Skip to content

Commit

Permalink
Added unit tests, fixed lack of 'Qu' tile
Browse files Browse the repository at this point in the history
  • Loading branch information
mwatson committed Jul 12, 2015
1 parent 459d623 commit 03d2de9
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 29 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
coverage/*
vendor/*
composer.lock
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": {
"BoggleSolver\\": "src/"
Expand Down
35 changes: 35 additions & 0 deletions examples/example02.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
header('Content-Type: text/plain; charset=utf-8');

// if you just use composer:
//include(__DIR__ . '/../vendor/autoload.php');

// if you don't have an autoloader for some reason
include(__DIR__ . '/../src/BoggleTile.php');
include(__DIR__ . '/../src/BoggleException.php');
include(__DIR__ . '/../src/BoggleSolver.php');

// testing a board with a Qu tile
$boggleBoard = "IZLEERCVADJQuITDI";

$boggle = new \BoggleSolver\BoggleSolver();

try {
$boggle->loadBoard($boggleBoard);
} catch (\BoggleSolver\BoggleException $e) {
die("exiting on error: " . $e->getMessage());
}

$words = $boggle->findWords();


echo "Boggle Board:\n\n";
echo $boggle->displayBoard();

echo "\n";

foreach ($words as $word) {
echo $word . "\n";
}

echo "\n";
7 changes: 7 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
PHPUNIT=vendor/phpunit/phpunit/phpunit

tests: tests/* src/* ; $(PHPUNIT) tests

coverage: tests/* src/* ; $(PHPUNIT) --coverage-html coverage tests

clean: ; rm -rf coverage
47 changes: 18 additions & 29 deletions src/BoggleSolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class BoggleSolver

public $lastSolveTime;

// for now, 3 - 12 letter words only
// for now, 3 - 11 letter words only
public static $minLen = 3;
public static $maxLen = 11;

Expand All @@ -28,7 +28,7 @@ public function __construct()

public function getWords()
{
return explode("\r\n", file_get_contents(__DIR__ . static::$dictFile));
return explode("\r\n", static::getDictFileContents());
}

public function loadDict()
Expand Down Expand Up @@ -65,6 +65,10 @@ public function loadDict()
}

$letter = $word[$i];
if ($letter == 'Q' && $word[$i + 1] == 'U') {
$letter = 'Qu';
$i++;
}
if (!isset($ptr[$letter])) {
$ptr[$letter] = array();
}
Expand All @@ -78,10 +82,14 @@ public function loadDict()
public function loadBoard($board)
{
// the board can be any string, non alpha chars will be stripped
$board = preg_replace("|[^A-Za-z]|", '', $board);
$board = strtoupper(preg_replace("|[^A-Za-z]|", '', $board));

$this->size = 0;

if (strpos($board, 'QU') !== false) {
$board = str_replace('QU', 'Q', $board);
}

// currently only 3x3 and 4x4 are suported
switch (strlen($board)) {
case 9:
Expand All @@ -92,11 +100,11 @@ public function loadBoard($board)
break;
default:
throw new BoggleException("Unknown board size of " . strlen($board));

}

for ($i = 0; $i < strlen($board); $i++) {
$this->board[] = new BoggleTile($board[$i], $i);
$letter = $board[$i] == 'Q' ? 'Qu' : $board[$i];
$this->board[] = new BoggleTile($letter, $i);
$this->boardLookup[$board[$i]] = true;
}

Expand Down Expand Up @@ -158,30 +166,6 @@ public function displayBoard($lineEnd = "\n")
return $board;
}

public function traverseBoard($callback)
{
$ptr = &$this->board[0];
$dir = "e";

while (1) {

$callback($ptr);

if ($ptr->$dir === null && $ptr->s === null) {
break;
}

if ($ptr->$dir !== null) {
$ptr = &$ptr->$dir;
} else if ($ptr->s !== null) {
$ptr = &$ptr->s;
$dir = $dir == "e" ? "w" : "e";
} else {
break;
}
}
}

public function findWords()
{
$start = microtime(true);
Expand Down Expand Up @@ -279,4 +263,9 @@ public function findWordsFromOneTile($boardPtr = null, $dictPtr = null, $words =

return $words;
}

public static function getDictFileContents()
{
return file_get_contents(__DIR__ . static::$dictFile);
}
}
151 changes: 151 additions & 0 deletions tests/BoggleSolver/BoggleSolverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace BoggleSolver;

class BoggleSolverTest extends \PHPUnit_Framework_TestCase
{
public $lessWords = array(
"ABCD", "ABC", "ABBA", "ABABA", "ABQU", "XYZ", "ABBBBBBBBBBBBBBBB", "AB"
);

public function testLoadDictBuildsDictionary()
{
$solverMock = $this->getMockBuilder('\BoggleSolver\BoggleSolver')
->setMethods(array('getWords'))
->getMock();

$solverMock->expects($this->once())
->method('getWords')
->will($this->returnValue($this->lessWords));

$solverMock->boardLookup = array('A' => 1);
$solverMock->size = 3;

$solverMock->loadDict();

$expectedDict = array(
'A' => array(
'B' => array(
'C' => array(
"ABC",
'D' => array(
"ABCD",
),
),
'B' => array(
'A' => array(
"ABBA",
),
),
'A' => array(
'B' => array(
'A' => array(
"ABABA",
),
),
),
'Qu' => array(
"ABQU"
),
),
),
);

$this->assertEquals($expectedDict, $solverMock->dict);
}

public function testLoadBoardLoadsBoard()
{
$exampleBoard = "A B C\nX Y Z\nQu R S";

$solverMock = $this->getMockBuilder('\BoggleSolver\BoggleSolver')
->setMethods(array('loadDict'))
->getMock();

$solverMock->expects($this->once())
->method('loadDict');

$solverMock->loadBoard($exampleBoard);

$expectedLookup = array(
'A', 'B', 'C', 'X', 'Y', 'Z', 'Q', 'R', 'S'
);

$this->assertEquals(3, $solverMock->size);

$this->assertEquals($expectedLookup, array_keys($solverMock->boardLookup));
}

public function testLoadBoardLoadsBoardOfFourByFour()
{
$exampleBoard = "A B C D\nX Y Z A\nQu R S T\nO K A Y";

$solverMock = $this->getMockBuilder('\BoggleSolver\BoggleSolver')
->setMethods(array('loadDict'))
->getMock();

$solverMock->expects($this->once())
->method('loadDict');

$solverMock->loadBoard($exampleBoard);

$this->assertEquals(4, $solverMock->size);

}

public function testLoadBoardThrowsExceptionOnUnknownBoardSize()
{
$exampleBoard = "A";

$this->setExpectedException('\BoggleSolver\BoggleException');

$solverMock = $this->getMockBuilder('\BoggleSolver\BoggleSolver')
->setMethods(array('loadDict'))
->getMock();

$solverMock->expects($this->never())
->method('loadDict');

$solverMock->loadBoard($exampleBoard);
}

public function testDisplayBoardReturnsBoard()
{
$solverMock = $this->getMockBuilder('\BoggleSolver\BoggleSolver')
->setMethods(array('loadDict'))
->getMock();

$solverMock->expects($this->once())
->method('loadDict');

$solverMock->loadBoard("ABCDEFGHI");

$result = $solverMock->displayBoard();

$this->assertEquals("ABC\nDEF\nGHI\n", $result);
}

public function testFindWords()
{
$solverMock = $this->getMockBuilder('\BoggleSolver\BoggleSolver')
->setMethods(array('getWords'))
->getMock();

$solverMock->expects($this->once())
->method('getWords')
->will($this->returnValue($this->lessWords));

$solverMock->boardLookup = array('A' => 1);
$solverMock->size = 3;

$solverMock->loadBoard("ABCABDABA");

$result = $solverMock->findWords();

$expected = array(
"ABC", "ABCD", "ABBA", "ABABA",
);

$this->assertEquals($expected, $result);
}
}
14 changes: 14 additions & 0 deletions tests/BoggleSolver/BoggleTileTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace BoggleSolver;

class BoggleTileTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$tile = new BoggleTile('A', 0);

$this->assertEquals('A', $tile->letter);
$this->assertEquals(0, $tile->id);
}
}

0 comments on commit 03d2de9

Please sign in to comment.