Skip to content

Commit

Permalink
Feature/castling ability in chess960 (#749)
Browse files Browse the repository at this point in the history
* Implemented Chess\Variant\Chess960\FEN\Str

* Optionally skip the shuffle array

* Updated CastlingRule

* Implemented the validation of the castling ability

* Added tests

* Implemented Chess\Variant\Chess960\FenToBoardFactory

* Refactored Chess\Variant\Chess960\FenToBoardFactory

* Refactored Chess\Variant\Chess960\FenToBoardFactory
  • Loading branch information
programarivm authored Feb 21, 2025
1 parent a015be5 commit b39a6de
Show file tree
Hide file tree
Showing 6 changed files with 1,328 additions and 9 deletions.
14 changes: 14 additions & 0 deletions src/Variant/Chess960/CastlingRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Chess\Variant\Chess960;

use Chess\Exception\UnknownNotationException;
use Chess\Variant\RandomCastlingRuleTrait;
use Chess\Variant\Classical\PGN\Square;
use Chess\Variant\Classical\CastlingRule as ClassicalCastlingRule;
Expand All @@ -24,4 +25,17 @@ public function __construct(array $shuffle = [])

$this->sq()->moveSqs();
}

public function validate(string $castlingAbility): string
{
if ($castlingAbility === self::NEITHER) {
return $castlingAbility;
} elseif (preg_match('/^K?Q?k?q?$/', $castlingAbility)) {
return $castlingAbility;
} elseif (preg_match('/^[A-H]{0,1}[A-H]{0,1}[a-h]{0,1}[a-h]{0,1}$/', $castlingAbility)) {
return $castlingAbility;
}

throw new UnknownNotationException();
}
}
38 changes: 38 additions & 0 deletions src/Variant/Chess960/FEN/Str.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Chess\Variant\Chess960\FEN;

use Chess\Exception\UnknownNotationException;
use Chess\Variant\Chess960\CastlingRule;
use Chess\Variant\Classical\FEN\PiecePlacement;
use Chess\Variant\Classical\FEN\Str as ClassicalStr;
use Chess\Variant\Classical\PGN\Color;
use Chess\Variant\Classical\PGN\Square;

class Str extends ClassicalStr
{
public function validate(string $string): string
{
$fields = explode(' ', $string);

if (!isset($fields[0]) ||
!isset($fields[1]) ||
!isset($fields[2]) ||
!isset($fields[3])
) {
throw new UnknownNotationException();
}

(new PiecePlacement())->validate($fields[0]);

(new Color())->validate($fields[1]);

(new CastlingRule())->validate($fields[2]);

if ('-' !== $fields[3]) {
(new Square())->validate($fields[3]);
}

return $string;
}
}
59 changes: 55 additions & 4 deletions src/Variant/Chess960/FenToBoardFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
use Chess\Exception\UnknownNotationException;
use Chess\Variant\AbstractBoard;
use Chess\Variant\PieceArrayFactory;
use Chess\Variant\Classical\FEN\Str;
use Chess\Variant\Chess960\FEN\Str;
use Chess\Variant\Classical\FenToBoardFactory as ClassicalFenToBoardFactory;
use Chess\Variant\Classical\PGN\Piece;
use Chess\Variant\Classical\PGN\Square;

/**
Expand All @@ -31,22 +32,72 @@ public static function create(string $string = null): AbstractBoard
$fields = array_filter(explode(' ', $string));
$namespace = 'Classical';
$shuffle = (new Shuffle())->extract($string);
$castlingRule = new CastlingRule($shuffle);

try {
$pieces = PieceArrayFactory::create(
$fenStr->toArray($fields[0]),
new Square(),
new CastlingRule($shuffle),
$castlingRule,
$namespace
);
$board = new Board($shuffle, $pieces, $fields[2]);
$castlingAbility = self::replaceChars($fields[2], $castlingRule);
$board = new Board($shuffle, $pieces, $castlingAbility);
$board->turn = $fields[1];
$board->startFen = $string;
$board->startFen = "{$fields[0]} {$fields[1]} {$castlingAbility} {$fields[3]}";
ClassicalFenToBoardFactory::enPassant($fields, $board);
} catch (\Throwable $e) {
throw new UnknownNotationException();
}

return $board;
}

public static function replaceChars(string $castlingAbility, $castlingRule)
{
$replaced = '';
foreach (str_split($castlingAbility) as $val) {
if (ctype_upper($val)) {
if ($val === 'K' || $val === 'Q') {
$replaced .= $val;
} elseif (self::isLikeQ($val, $castlingRule)) {
$replaced .= 'Q';
} elseif (self::isLikeK($val, $castlingRule)) {
$replaced .= 'K';
}
} else {
if ($val === 'k' || $val === 'q') {
$replaced .= $val;
} elseif (self::isLikeQ($val, $castlingRule)) {
$replaced .= 'q';
} elseif (self::isLikeK($val, $castlingRule)) {
$replaced .= 'k';
}
}
}

return $replaced;
}

public static function isLikeQ(string $char, $castlingRule): bool
{
foreach ($castlingRule->startFiles as $key => $val) {
if ($val === Piece::K) {
return mb_strtolower($char) > $key;
}
}

return false;
}

public static function isLikeK(string $char, $castlingRule): bool
{
foreach ($castlingRule->startFiles as $key => $val) {
if ($val === Piece::K) {
return mb_strtolower($char) < $key;
}
}

return false;
}
}
6 changes: 3 additions & 3 deletions src/Variant/RandomCastlingRuleTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

trait RandomCastlingRuleTrait
{
protected array $shuffle;
public array $shuffle;

protected array $startFiles;
public array $startFiles;

protected array $size;
public array $size;

protected function sq()
{
Expand Down
Loading

0 comments on commit b39a6de

Please sign in to comment.