Generate random chess position: Difference between revisions
Content added Content deleted
(improved function pawn_on_extreme_ranks) |
|||
Line 556: | Line 556: | ||
Uses System.Random library: https://hackage.haskell.org/package/random-1.1/docs/System-Random.html |
Uses System.Random library: https://hackage.haskell.org/package/random-1.1/docs/System-Random.html |
||
Module RandomChess |
|||
<lang haskell>{-# LANGUAGE LambdaCase, TupleSections #-} |
|||
module RandomChess |
|||
<lang haskell>{-# LANGUAGE LambdaCase #-} |
|||
( placeKings |
|||
, placePawns |
|||
, placeRemaining |
|||
, boardSort |
|||
, emptyBoard |
|||
, toFen |
|||
, ChessBoard |
|||
, Square (..) |
|||
, Pos |
|||
, BoardState (..) |
|||
, getBoard |
|||
) |
|||
where |
|||
import |
import Control.Monad.State (State, get, gets, put) |
||
import Data.List (find, sortBy) |
import Data.List (find, sortBy) |
||
import |
import System.Random (Random, StdGen, random, randomR) |
||
type Pos = (Char, Int) |
type Pos = (Char, Int) |
||
Line 576: | Line 591: | ||
type PieceCount = [(Square, Int)] |
type PieceCount = [(Square, Int)] |
||
data BoardState = BoardState { board :: ChessBoard , generator :: StdGen } |
|||
instance Show Square where |
instance Show Square where |
||
Line 595: | Line 612: | ||
randomR (a, b) g = case randomR (fromEnum a, fromEnum b) g of |
randomR (a, b) g = case randomR (fromEnum a, fromEnum b) g of |
||
(x, g'') -> (toEnum x, g'') |
(x, g'') -> (toEnum x, g'') |
||
random |
random = randomR (minBound, maxBound) |
||
instance Random PieceColor where |
instance Random PieceColor where |
||
randomR (a, b) g = case randomR (fromEnum a, fromEnum b) g of |
randomR (a, b) g = case randomR (fromEnum a, fromEnum b) g of |
||
(x, g'') -> (toEnum x, g'') |
(x, g'') -> (toEnum x, g'') |
||
random |
random = randomR (minBound, maxBound) |
||
fullBoard :: PieceCount |
fullBoard :: PieceCount |
||
Line 620: | Line 637: | ||
emptyBoard :: ChessBoard |
emptyBoard :: ChessBoard |
||
emptyBoard = |
emptyBoard = fmap (EmptySquare,) . (,) <$> ['a'..'h'] <*> [1..8] |
||
replaceSquareByPos :: (Square, Pos) -> ChessBoard -> ChessBoard |
replaceSquareByPos :: (Square, Pos) -> ChessBoard -> ChessBoard |
||
replaceSquareByPos e@( |
replaceSquareByPos e@(_, p) = fmap (\x -> if p == snd x then e else x) |
||
isPosOccupied :: Pos -> ChessBoard -> Bool |
isPosOccupied :: Pos -> ChessBoard -> Bool |
||
Line 632: | Line 649: | ||
isAdjacent :: Pos -> Pos -> Bool |
isAdjacent :: Pos -> Pos -> Bool |
||
isAdjacent ( |
isAdjacent (x1, y1) (x2, y2) = |
||
let upOrDown = (pred |
let upOrDown = (pred y1 == y2 || succ y1 == y2) |
||
leftOrRight = (pred |
leftOrRight = (pred x1 == x2 || succ x1 == x2) |
||
in ( |
in (x2 == x1 && upOrDown) |
||
|| (pred |
|| (pred x1 == x2 && upOrDown) |
||
|| (succ |
|| (succ x1 == x2 && upOrDown) |
||
|| (leftOrRight && |
|| (leftOrRight && y1 == y2) |
||
fen :: Square -> String |
fen :: Square -> String |
||
Line 655: | Line 672: | ||
boardSort :: (Square, Pos) -> (Square, Pos) -> Ordering |
boardSort :: (Square, Pos) -> (Square, Pos) -> Ordering |
||
boardSort (_, ( |
boardSort (_, (x1, y1)) (_, (x2, y2)) | y1 < y2 = GT |
||
| |
| y1 > y2 = LT |
||
| |
| y1 == y2 = compare x1 x2 |
||
toFen :: ChessBoard -> String |
toFen :: ChessBoard -> String |
||
toFen [] = " w - - 0 1" <> [] |
toFen [] = " w - - 0 1" <> [] |
||
toFen b |
toFen b = scanRow (fst <$> take 8 b) 0 |
||
where |
|||
scanRow [] |
scanRow [] 0 = nextRow |
||
scanRow [] |
scanRow [] n = show n <> nextRow |
||
scanRow |
scanRow (EmptySquare:xs) n = scanRow xs (succ n) |
||
scanRow (x |
scanRow (x:xs) 0 = nextPiece x xs |
||
scanRow (x |
scanRow (x:xs) n = show n <> nextPiece x xs |
||
nextRow = "/" <> toFen (drop 8 b) |
|||
nextPiece x xs = fen x <> scanRow xs 0 |
|||
-- State functions |
|||
nextRow = "/" <> toFen (drop 8 b) |
|||
randomPos :: State BoardState Pos |
|||
nextPiece x xs = fen x <> scanRow xs 0 |
|||
randomPos = do |
|||
currentState <- get |
|||
let gen1 = generator currentState |
|||
let (num, gen2) = randomR (1, 8) gen1 |
|||
let (chr, gen3) = randomR ('a', 'h') gen2 |
|||
put (currentState {generator = gen3}) |
|||
let pos = (chr, num) |
|||
if isPosOccupied pos (board currentState) then |
|||
randomPos |
|||
else |
|||
pure pos |
|||
randomPiece :: State BoardState Square |
|||
-- IO functions |
|||
randomPiece = do |
|||
currentState <- get |
|||
let gen1 = generator currentState |
|||
let (rank, gen2) = random gen1 |
|||
let (color, gen3) = random gen2 |
|||
put (currentState {generator = gen3}) |
|||
pure $ ChessPiece rank color |
|||
placeKings :: State BoardState () |
|||
randomPos :: ChessBoard -> IO Pos |
|||
placeKings = do |
|||
randomPos b = pos >>= \p -> if isPosOccupied p b then randomPos b else pos |
|||
currentState <- get |
|||
where |
|||
p1 <- randomPos |
|||
pos = (,) <$> chr <*> num |
|||
p2 <- randomPos |
|||
num = getStdRandom $ randomR (1, 8) |
|||
chr = getStdRandom $ randomR ('a', 'h') |
|||
randomPiece :: IO Square |
|||
randomPiece = ChessPiece <$> rank <*> color |
|||
where |
|||
rank = getStdRandom random |
|||
color = getStdRandom random |
|||
placeKings :: ChessBoard -> IO ChessBoard |
|||
placeKings b = do |
|||
p1 <- randomPos b |
|||
p2 <- randomPos b |
|||
if p1 `isAdjacent` p2 || p1 == p2 |
if p1 `isAdjacent` p2 || p1 == p2 |
||
then placeKings |
then placeKings |
||
else |
else do |
||
let updatedBoard = replaceSquareByPos (ChessPiece King White, p1) $ |
|||
replaceSquareByPos (ChessPiece King Black, p2) (board currentState) |
|||
put currentState { board = updatedBoard } |
|||
) |
|||
placePawns :: |
placePawns :: State BoardState () |
||
placePawns |
placePawns = do |
||
currentState <- get |
|||
where |
|||
let gen1 = generator currentState |
|||
go :: ChessBoard -> Int -> IO ChessBoard |
|||
let (totalPawns, gen2) = randomR (1, 16) gen1 |
|||
go b'' 0 = pure b'' |
|||
put currentState { generator = gen2 } |
|||
go b'' n = do |
|||
go totalPawns |
|||
pos <- randomPos b'' |
|||
where |
|||
pawn@(ChessPiece _ color) <- randomPawn |
|||
go :: Int -> State BoardState () |
|||
go 0 = pure () |
|||
|| isPosOccupied pos b'' |
|||
go n = do |
|||
|| enpassant color == snd pos |
|||
currentState <- get |
|||
|| firstPos color == snd pos |
|||
let gen1 = generator currentState |
|||
pos <- randomPos |
|||
else go (replaceSquareByPos (pawn, pos) b'') (pred n) |
|||
let (color, gen2) = random gen1 |
|||
promoted White = 8 |
|||
put currentState { generator = gen2 } |
|||
promoted Black = 1 |
|||
let pawn = ChessPiece Pawn color |
|||
enpassant White = 5 |
|||
let currentBoard = board currentState |
|||
enpassant Black = 4 |
|||
if promoted color == snd pos || isPosOccupied pos currentBoard || |
|||
firstPos White = 1 |
|||
enpassant color == snd pos || firstPos color == snd pos |
|||
firstPos Black = 8 |
|||
then go n |
|||
else do |
|||
put currentState { board = replaceSquareByPos (pawn, pos) currentBoard } |
|||
go $ pred n |
|||
promoted White = 8 |
|||
promoted Black = 1 |
|||
enpassant White = 5 |
|||
enpassant Black = 4 |
|||
firstPos White = 1 |
|||
firstPos Black = 8 |
|||
placeRemaining :: State BoardState () |
|||
num = getStdRandom (randomR (1, 16)) |
|||
placeRemaining = do |
|||
currentState <- get |
|||
let gen1 = generator currentState |
|||
let (n, gen2) = randomR (5, sum $ fmap snd remaining) gen1 |
|||
put currentState { generator = gen2 } |
|||
go remaining n |
|||
where |
|||
remaining = filter (\case |
|||
(ChessPiece King _, _) -> False |
|||
(ChessPiece Pawn _, _) -> False |
|||
(EmptySquare, _) -> False |
|||
_ -> True) |
|||
fullBoard |
|||
go :: [(Square, Int)] -> Int -> State BoardState () |
|||
randomPawn = ChessPiece Pawn <$> rColor where rColor = getStdRandom (random) |
|||
go _ 0 = pure () |
|||
go remaining n = do |
|||
currentState <- get |
|||
let currentBoard = board currentState |
|||
position <- randomPos |
|||
piece <- randomPiece |
|||
if not (isPermitted piece) || isPosOccupied position currentBoard |
|||
then go remaining n |
|||
else do |
|||
let updatedBoard = replaceSquareByPos (piece, position) currentBoard |
|||
put currentState { board = updatedBoard } |
|||
go (consume piece remaining) (pred n) |
|||
where |
|||
isPermitted p = |
|||
case find ((==p) . fst) remaining of |
|||
Just (_, count) -> count > 0 |
|||
Nothing -> False |
|||
consume p'' = fmap (\(p, c) -> if p == p'' then (p, pred c) else (p, c)) |
|||
getBoard :: State BoardState ChessBoard |
|||
getBoard = gets (sortBy boardSort . board)</lang> |
|||
placeRemaining b = do |
|||
n <- num (sum $ map snd remaining) |
|||
putStrLn $ "Placing " <> show n |
|||
<> " more random positions after placing 2 kings and " |
|||
<> (show $ totalPawns b) <> " pawns. " |
|||
go remaining b n |
|||
where |
|||
remaining :: [(Square, Int)] |
|||
remaining = filter |
|||
(\case |
|||
((ChessPiece King _), _) -> False |
|||
((ChessPiece Pawn _), _) -> False |
|||
(EmptySquare , _) -> False |
|||
_ -> True |
|||
) |
|||
fullBoard |
|||
num mx = getStdRandom $ randomR (5, mx) |
|||
totalPawns = length . filter |
|||
(\case ((ChessPiece Pawn _), _) -> True |
|||
_ -> False |
|||
) |
|||
Module Main |
|||
go :: [(Square, Int)] -> ChessBoard -> Int -> IO ChessBoard |
|||
<lang haskell>module Main where |
|||
go _ b'' 0 = pure b'' |
|||
go permitted b'' n = do |
|||
position <- randomPos b'' |
|||
piece <- randomPiece |
|||
if (not $ isPermitted piece) || isPosOccupied position b'' |
|||
then go permitted b'' n |
|||
else go (consume piece permitted) |
|||
(replaceSquareByPos (piece, position) b'') |
|||
(pred n) |
|||
import Control.Monad.State (evalState) |
|||
where |
|||
import RandomChess (ChessBoard, emptyBoard, placeKings, getBoard, |
|||
isPermitted p = case find (\x -> fst x == p) permitted of |
|||
placePawns, placeRemaining, toFen, BoardState (..)) |
|||
Just (_, count) -> count > 0 |
|||
import System.Random (newStdGen) |
|||
Nothing -> False |
|||
consume p'' = map (\(p, c) -> if p == p'' then (p, pred c) else (p, c)) |
|||
draw :: ChessBoard -> IO () |
draw :: ChessBoard -> IO () |
||
draw b = do |
draw b = do |
||
showXAxis |
showXAxis >> drawLine |
||
mapM_ (\b@(p, (x,y)) -> |
|||
line |
|||
case x of 'h' -> putStr (" | " <> show p <> " | " <> show y <> "\n") >> drawLine |
|||
mapM_ |
|||
'a' -> putStr (show y <> " | " <> show p) |
|||
(\b@(p, (x, y)) -> case x of |
|||
_ -> putStr (" | " <> show p) |
|||
) |
|||
'a' -> putStr (show y <> " | " <> show p) |
|||
b |
|||
_ -> putStr (" | " <> show p) |
|||
showXAxis >> putStrLn "" >> putStrLn (toFen b) |
|||
) |
|||
sorted |
|||
showXAxis |
|||
putStrLn "" |
|||
putStrLn $ toFen sorted |
|||
-- mapM_ print $ sortBy (comparing fst) $ filter (\(s, _) -> s /= EmptySquare) b |
|||
where |
where |
||
drawLine = putStrLn (" " <> replicate 33 '-') |
|||
sorted = sortBy boardSort b |
|||
line = putStrLn (" " <> (replicate 33 '-')) |
|||
showXAxis = do |
showXAxis = do |
||
putStr " " |
putStr " " |
||
mapM_ (\(_, (x, _)) -> putStr $ " " <> [x]) (take 8 |
mapM_ (\(_, (x, _)) -> putStr $ " " <> [x]) (take 8 b) |
||
putStrLn "" |
putStrLn "" |
||
main :: IO () |
main :: IO () |
||
main = do |
|||
main = placeKings emptyBoard >>= placePawns >>= placeRemaining >>= draw</lang> |
|||
stdGen <- newStdGen |
|||
draw $ evalState buildBoard (BoardState emptyBoard stdGen) |
|||
where |
|||
buildBoard = placeKings >> placePawns >> placeRemaining >> getBoard</lang> |
|||
{{out}} |
{{out}} |
||
Run 1 |
Run 1 |
||
<pre> a b c d e f g h |
|||
<pre> |
|||
Placing 8 more random positions after placing 2 kings and 11 pawns. |
|||
a b c d e f g h |
|||
--------------------------------- |
--------------------------------- |
||
8 | | | |
8 | | | ♜ | ♘ | | | ♞ | | 8 |
||
--------------------------------- |
--------------------------------- |
||
7 | ♙ | |
7 | ♙ | ♟ | ♟ | ♔ | ♙ | ♙ | | ♙ | 7 |
||
--------------------------------- |
--------------------------------- |
||
6 | | | | |
6 | | | | ♛ | | | | | 6 |
||
--------------------------------- |
--------------------------------- |
||
5 | |
5 | ♘ | | | | | | | ♟ | 5 |
||
--------------------------------- |
--------------------------------- |
||
4 | | | |
4 | | | ♙ | | | ♗ | ♙ | | 4 |
||
--------------------------------- |
--------------------------------- |
||
3 | |
3 | ♟ | ♕ | ♜ | ♟ | | ♗ | ♚ | | 3 |
||
--------------------------------- |
--------------------------------- |
||
2 | |
2 | ♞ | | | | ♙ | | ♙ | ♟ | 2 |
||
--------------------------------- |
--------------------------------- |
||
1 | | | |
1 | | | ♖ | | | | | | 1 |
||
--------------------------------- |
--------------------------------- |
||
a b c d e f g h |
a b c d e f g h |
||
2rN2n1/PppKPP1P/3q4/N6p/2P2BP1/pQrp1Bk1/n3P1Pp/2R5/ w - - 0 1</pre> |
|||
</pre> |
|||
Run 2 |
Run 2 |
||
<pre> a b c d e f g h |
|||
<pre> |
|||
Placing 9 more random positions after placing 2 kings and 2 pawns. |
|||
a b c d e f g h |
|||
--------------------------------- |
--------------------------------- |
||
8 | | |
8 | | | | | ♚ | | | | 8 |
||
--------------------------------- |
--------------------------------- |
||
7 | |
7 | ♟ | ♔ | ♙ | | | | | | 7 |
||
--------------------------------- |
--------------------------------- |
||
6 | |
6 | ♟ | | ♜ | ♟ | | | | | 6 |
||
--------------------------------- |
--------------------------------- |
||
5 | | | |
5 | | | ♟ | | | | | | 5 |
||
--------------------------------- |
--------------------------------- |
||
4 | | |
4 | | | | | | | | | 4 |
||
--------------------------------- |
--------------------------------- |
||
3 | | |
3 | | ♟ | | | ♙ | | | ♛ | 3 |
||
--------------------------------- |
--------------------------------- |
||
2 | | | |
2 | | | | | ♟ | | | ♘ | 2 |
||
--------------------------------- |
--------------------------------- |
||
1 | |
1 | ♖ | ♖ | | | ♝ | | | | 1 |
||
--------------------------------- |
--------------------------------- |
||
a b c d e f g h |
a b c d e f g h |
||
4k3/pKP5/p1rp4/2p5/8/1p2P2q/4p2N/RR2b3/ w - - 0 1</pre> |
|||
</pre> |
|||
etc... |
etc... |
||