Skip to main content
deleted 32 characters in body
Source Link
Marius
  • 18.5k
  • 4
  • 55
  • 103

2184 solutions. / 546 if you exclude rotations.

2184 solutions. / 546 if you exclude rotations.

2184 solutions.

Source Link
Marius
  • 18.5k
  • 4
  • 55
  • 103

Even if I was a few minutes late I still want to post this because I'm really proud of my work and I invested some time in it.
I've brute forced it and got

2184 solutions. / 546 if you exclude rotations.

I chose PHP as my weapon.
The script below can be extended to check for other pieces just by creating the additional classes like Queen, Bishop and implementing the method attack that returns true or false depending if the current position of a piece attacks or not a certain square.

<?php
    
class ReachedEndException extends Exception {}

class Point
{
    /**
     * @var int
     */
    private $x;
    /**
     * @var int
     */
    private $y;
    public function __construct($x, $y)
    {
        $this->setX($x);
        $this->setY($y);
    }

    /**
     * @return int
     */
    public function getX()
    {
        return $this->x;
    }

    /**
     * @param $x
     * @return $this
     */
    public function setX($x)
    {
        $this->x = $x;
        return $this;
    }

    /**
     * @return int
     */
    public function getY()
    {
        return $this->y;
    }

    /**
     * @param $y
     */
    public function setY($y)
    {
        $this->y = $y;
    }

    /**
     * @param Point $p
     * @return bool
     */
    public function equals(Point $p)
    {
        return ($this->getX() == $p->getX() && $this->getY() == $p->getY());
    }

    /**
     * used for debugging
     *
     * @return string
     */
    public function __toString()
    {
        return '('.$this->x.', '.$this->y.')';
    }
}

interface PieceInterface
{
    /**
     * @param Point $p
     * @return bool
     */
    public function attack(Point $p);
}

abstract class Piece implements PieceInterface
{
    /**
     * @var Point
     */
    protected $position;
    /**
     * @var string
     */
    protected $notation = '';

    /**
     * Piece constructor.
     * @param Point|null $position
     */
    public function __construct(Point $position = null)
    {
        $this->position = $position;
    }

    /**
     * @return Point
     */
    public function getPosition()
    {
        return $this->position;
    }

    /**
     * @return mixed
     */
    public function getNotation()
    {
        return $this->notation;
    }

    /**
     * @param Point $position
     */
    public function setPosition($position)
    {
        $this->position = $position;
    }

    /**
     * used for debugging
     * @return string
     */
    public function __toString()
    {
        return $this->notation.$this->getPosition();
    }

    public abstract function attack(Point $p);

}

class Rook extends Piece implements PieceInterface
{
    /**
     * @var string
     */
    protected $notation = 'R';

    /**
     * @param Point $p
     * @return bool
     */
    public function attack(Point $p)
    {
        if (!$this->position) {
            return false;
        }
        return ($this->getPosition()->getX() == $p->getX() || $this->getPosition()->getY() == $p->getY());
    }


}

class Knight extends Piece implements PieceInterface
{
    /**
     * @var string
     */
    protected $notation = 'K';

    /**
     * @param Point $p
     * @return bool
     */
    public function attack(Point $p)
    {
        if (!$this->position) {
            return false;
        }
        $thisX = $this->getPosition()->getX();
        $thisY = $this->getPosition()->getY();
        $pointX = $p->getX();
        $pointY = $p->getY();
        if (abs($thisX - $pointX) == 2 && abs($thisY - $pointY) == 1) {
            return true;
        }
        if (abs($thisX - $pointX) == 1 && abs($thisY - $pointY) == 2) {
            return true;
        }
        return false;
    }
}

class App
{
    /**
     * board X size
     * @var int
     */
    private $boardX;
    /**
     * board Y size
     * @var int
     */
    private $boardY;
    /**
     * pieces to add on the table
     * @var Piece[]
     */
    protected $piecesToAdd = array();
    /**
     * all solutions
     * @var string[]
     */
    protected $solutions = array();
    /**
     * already added pieces
     * @var Piece[]
     */
    private $pieces = array();

    /**
     * App constructor.
     * @param $boardX
     * @param null $boardY
     * @param $piecesToAdd
     */
    public function __construct($piecesToAdd, $boardX, $boardY = null)
    {
        if (is_null($boardY)) {
            $boardY = $boardX;
        }
        $this->boardX = $boardX;
        $this->boardY = $boardY;
        $this->piecesToAdd = $piecesToAdd;

    }

    /**
     * add a piece on the board
     * @param Piece $p
     * @param Point $point
     * @return bool
     */
    public function addPiece(Piece $p, Point $point)
    {
        $p->setPosition($point);
        $canPlace = true;
        //check of the piece does not attack the other set pieces or if it's not attacked by one
        foreach ($this->pieces as $piece) {
            if (
                $p->getPosition()->equals($piece->getPosition())
                || $p->attack($piece->getPosition()) ||
                $piece->attack($p->getPosition())
            ) {
                $canPlace = false;
            }
        }
        if ($canPlace) {
            $this->pieces[] = $p;
            $this->pieces = array_values($this->pieces);
            return true;
        } else {
            //if a piece cannot be added, move to the next position
            $nextPoint = $this->incrementPoint($point);
            if ($nextPoint) {
                return $this->addPiece($p, $nextPoint);
            }
        }
        //if there is no valid position return false
        return false;
    }

    /**
     * get the next position where you can add a piece if you need to move one
     * @return bool|Point
     */
    private function getNextPosition()
    {
        $nextPosition = false;
        while (!$nextPosition) {
            //remove last piece and increment the position for the previous one.
            $piece = end($this->pieces);
            if (!$piece) {
                throw new ReachedEndException();
            }
            $position = clone $piece->getPosition();
            $nextPosition = $this->incrementPoint($position);
            $this->removePiece(count($this->pieces) - 1);
        }
        return $nextPosition;
    }

    /**
     * do the dew
     * @return App
     */
    public function execute()
    {
        $currentPosition = new Point(0, 0);
        $added = true;
        while (true) {
            while ($added && !$this->isBoardFilled()) {
                $pieceToAdd = $this->piecesToAdd[count($this->pieces)];
                $added = $this->addPiece($pieceToAdd, $currentPosition);
                //if a piece was added start the next one from top left corner
                if ($added) {
                    $currentPosition = new Point(0, 0);
                }
            }
            //if we cannot add a piece, remove the last one(s) and check the next valid position
            if (!$added) {
                try {
                    $currentPosition = $this->getNextPosition();
                } catch (ReachedEndException $e) {
                    //it means we cannot move anything arround anymore
                    return $this;
                }
                $added = true;
            } elseif ($this->isBoardFilled()) {
                //if the board is in a valid position save the state
                $solution = $this->printBoardState();
                //remember a hash of the position to avoid duplicates
                $hash = sha1($solution);
                $this->solutions[$hash] = $solution;
                try {
                    $currentPosition = $this->getNextPosition();
                } catch (ReachedEndException $e) {
                    //it means we are done
                    return $this;
                }
            } else {
                $added = true;
                //start over
                $currentPosition = new Point(0, 0);
            }
        }
        return $this;
    }

    /**
     * form a readable string with the solutions
     * @return string
     */
    public function printSolutions()
    {
        $string = '';
        $increment = 1;
        foreach ($this->solutions as $solution) {
            $string .= 'Solution '.$increment++.'<br />'.$solution.'<br />';
        }
        return $string;
    }

    /**
     * remove a piece
     * @param $index
     * @return App
     */
    private function removePiece($index)
    {
        unset($this->pieces[$index]);
        return $this;
    }

    /**
     * check if all pieces are on the board
     * @return bool
     */
    public function isBoardFilled()
    {
        return count($this->pieces) == count($this->piecesToAdd);
    }

    /**
     * generate the board configuration string
     * @param string $positionSeparator
     * @param string $blank
     * @param string $lineSeparator
     * @return string
     */
    private function printBoardState($positionSeparator = '|', $blank = '--', $lineSeparator = '<br />')
    {
        $board = array();
        $text = '';
        foreach ($this->pieces as $piece) {
            $position = $piece->getPosition();
            $board[$position->getX()][$position->getY()] = $piece->getNotation();
        }
        for ($i = 0;$i<$this->boardX; $i++) {
            $text .= $positionSeparator;
            for ($j = 0;$j<$this->boardY; $j++) {
                if (isset($board[$i][$j])) {
                    $text .= $board[$i][$j];
                } else {
                    $text .= $blank;
                }
                $text .= $positionSeparator;
            }
            $text .= $lineSeparator;
        }
        return $text;
    }

    /**
     * get the next valid point
     * @param Point $p
     * @return bool|Point
     */
    private function incrementPoint(Point $p)
    {
        if ($p->getX() == $this->boardX - 1) {
            if ($p->getY() == $this->boardY - 1) {
                return false; //reached the end
            }
            return new Point(0, $p->getY() + 1);
        }
        return new Point($p->getX()+1, $p->getY());
    }

    /**
     * take a whild guess
     * @return string
     */
    public function debug()
    {
        $string = "state: ";
        foreach ($this->pieces as $piece) {
            $string.= ' '.$piece.'--';
        }
        return $string.'<br />';
    }
}

//configure the pieces to be added
$pieces = array(
    new Rook(),
    new Rook(),
    new Knight(),
    new Knight()
);
$boardSize = 5;
$app = new App($pieces, $boardSize);
//run it
$app->execute();
//see if it worked
echo $app->printSolutions();

you can give it a try by copy/pasting it into http://phpfiddle.org/.

You will see results like:

Solution 1  
|R |--|--|--|--|  
|--|R |--|--|--|  
|--|--|K |--|--|  
|--|--|--|--|--|  
|--|--|K |--|--|  

or

Solution 481
|--|--|--|--|--|
|--|K |--|--|--|
|R |--|--|--|--|
|--|--|--|R |--|
|--|--|K |--|--|

Just chose 2 at random.