feat: Initial commit + Position & FieldBitMap classes

This commit is contained in:
overflowerror 2023-12-25 22:53:38 +01:00
commit 85fa12c08f
8 changed files with 1840 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
vendor/

20
composer.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "overflowerror/php-chess",
"description": "description",
"minimum-stability": "stable",
"license": "MIT",
"authors": [
{
"name": "overflowerror",
"email": "email@example.com"
}
],
"require-dev": {
"phpunit/phpunit": "^10.5"
},
"autoload": {
"psr-4": {
"Game\\": "./src/Game/"
}
}
}

1632
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

12
phpunit.xml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<phpunit bootstrap="./vendor/autoload.php"
displayDetailsOnTestsThatTriggerDeprecations="true"
displayDetailsOnTestsThatTriggerErrors="true"
displayDetailsOnTestsThatTriggerNotices="true"
displayDetailsOnTestsThatTriggerWarnings="true">
<testsuites>
<testsuite name="The project's test suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>

71
src/Game/FieldBitMap.php Normal file
View file

@ -0,0 +1,71 @@
<?php
namespace Game;
if (gettype(2147483648) == "double") {
die("need 64 bit integers");
}
class FieldBitMap {
private int $map = 0;
public function __construct(array|int $argument) {
if (gettype($argument) == "array") {
foreach ($argument as $position) {
$this->add($position);
}
} else {
$this->map = $argument;
}
}
public function getBitForPosition(Position $position): int {
return $position->file * 8 + $position->rank;
}
public function add(Position $position): void {
if ($position->isValid()) {
$this->map |= 1 << $this->getBitForPosition($position);
}
}
public function intersect(FieldBitMap $map): FieldBitMap {
return new FieldBitMap($this->map & $map->map);
}
public function union(FieldBitMap $map): FieldBitMap {
return new FieldBitMap($this->map | $map->map);
}
public function isEmpty(): bool {
return $this->map == 0;
}
public function has(Position $position): bool {
return ($this->map & (1 << $this->getBitForPosition($position))) != 0;
}
public function getPositions(): array {
$result = [];
for ($i = 0; $i < 64; $i++) {
if ($this->map & (1 << $i)) {
$result[] = new Position(floor($i / 8), $i % 8);
}
}
return $result;
}
public function getMap(): int {
return $this->map;
}
public static function full(): FieldBitMap {
return new FieldBitMap(-1);
}
public static function empty(): FieldBitMap {
return new FieldBitMap(0);
}
}

23
src/Game/Position.php Normal file
View file

@ -0,0 +1,23 @@
<?php
namespace Game;
class Position {
public int $file;
public int $rank;
public function __construct($file, $rank) {
$this->rank = $rank;
$this->file = $file;
}
public function isValid(): bool {
return
$this->file >= 0 && $this->file < 8 &&
$this->rank >= 0 && $this->rank < 8;
}
public function __toString(): string {
return ["a", "b", "c", "d", "e", "f", "g", "h"][$this->file] . ($this->rank + 1);
}
}

View file

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
use Game\FieldBitMap;
use Game\Position;
use PHPUnit\Framework\TestCase;
final class FieldBitMapTest extends TestCase {
public function testEmptyMapHasPositions() {
$subject = FieldBitMap::empty();
$this->assertTrue($subject->isEmpty());
$this->assertEmpty($subject->getPositions());
}
public function testFullMapHasAllPositions() {
$subject = FieldBitMap::full();
$this->assertFalse($subject->isEmpty());
$this->assertCount(64, $subject->getPositions());
}
public function testMapPositionsAreRetained() {
$positions = [
new Position(0, 0),
new Position(3, 4),
new Position(7, 7),
];
$subject = new FieldBitMap($positions);
$this->assertFalse($subject->isEmpty());
$this->assertEquals($positions, $subject->getPositions());
}
}

View file

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
use Game\Position;
use PHPUnit\Framework\TestCase;
final class PositionTest extends TestCase {
public function testIsValid_default_min() {
$subject = new Position(0, 0);
$this->assertTrue($subject->isValid());
}
public function testIsValid_default_max() {
$subject = new Position(7, 7);
$this->assertTrue($subject->isValid());
}
public function testIsValid_fileTooHigh() {
$subject = new Position(8, 7);
$this->assertFalse($subject->isValid());
}
public function testIsValid_RankTooHigh() {
$subject = new Position(7, 8);
$this->assertFalse($subject->isValid());
}
public function testIsValid_fileTooLow() {
$subject = new Position(-1, 0);
$this->assertFalse($subject->isValid());
}
public function testIsValid_RankTooLow() {
$subject = new Position(0, -1);
$this->assertFalse($subject->isValid());
}
public function testToString() {
$subject = new Position(4, 5);
$this->assertEquals("e6", strval($subject));
}
}