Skip to content

Commit bd6fe2d

Browse files
committed
Document functions in Position module
1 parent 87f612f commit bd6fe2d

File tree

1 file changed

+75
-35
lines changed

1 file changed

+75
-35
lines changed

src/Position.elm

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module Position exposing
2020
It also includes operations on other types that needs the Position, to avoid circular dependencies.
2121
2222
There's a lot going on in here:
23+
2324
- the whole game of chess
2425
- representing moves as strings
2526
- rudimentary "AI"
@@ -29,6 +30,7 @@ The API exposed by this module and its do-everything nature have a lot of room f
2930
The state of the game is represented by the piece positions, the move history, and the current player.
3031
Since we have to frequently iterate over the History to figure things out if things like castling are possible,
3132
this format is pretty inefficient. So far, this only starts to appear when evaluating moves for the AI.
33+
3234
-}
3335

3436
import Array
@@ -74,12 +76,16 @@ initial =
7476
}
7577

7678

79+
{-| Get the piece at the given square
80+
-}
7781
get : Position -> Square -> Maybe Piece
7882
get { board } { rank, file } =
7983
Array2D.get rank file board
8084
|> Maybe.withDefault Nothing
8185

8286

87+
{-| Find the squares with pieces of the same kind belonging to the same player
88+
-}
8389
findPieces : Piece -> Position -> List Square
8490
findPieces piece position =
8591
let
@@ -97,6 +103,10 @@ findPieces piece position =
97103
List.filter (hasPiece position) occupiedSquares
98104

99105

106+
{-| Used to test if a potential ply is can be applied to the current Position.
107+
Intended to be used when parsing plies from text, as the parser is only looking
108+
at syntactic correctness.
109+
-}
100110
isPlyValid : Ply -> Position -> Result String ()
101111
isPlyValid ply position =
102112
let
@@ -116,6 +126,9 @@ isPlyValid ply position =
116126
Err "Illegal ply"
117127

118128

129+
{-| If the current player has a pawn that can move to the provided square,
130+
get the square that it is on.
131+
-}
119132
findPawnThatCanMoveToSquare : Square -> Position -> Maybe Square
120133
findPawnThatCanMoveToSquare square position =
121134
let
@@ -239,6 +252,8 @@ areSquaresUnoccupied position squares =
239252
List.map (get position) squares |> List.filterMap identity |> List.isEmpty
240253

241254

255+
{-| Get every square that the current player has a piece on
256+
-}
242257
getSquaresOccupiedByCurrentPlayer : Position -> EverySet Square
243258
getSquaresOccupiedByCurrentPlayer position =
244259
getSquaresOccupiedByPlayer position.playerToMove position
@@ -355,13 +370,18 @@ isPlayerInCheck player position =
355370
EverySet.member sq otherPlayerPossibleMoves
356371

357372

373+
{-| Determine if the game has ended by checkmate.
374+
-}
358375
isCurrentPlayerInCheckMate : Position -> Bool
359376
isCurrentPlayerInCheckMate position =
360377
isPlayerInCheck position.playerToMove position
361378
&& EverySet.empty
362379
== generateAllMovesForCurrentPlayerWithoutCheck position
363380

364381

382+
{-| Determine if the game has ended by stalemate. A stalemate occurs when the current player is not in check,
383+
but they have no legal moves.
384+
-}
365385
isStalemate : Position -> Bool
366386
isStalemate position =
367387
not (isPlayerInCheck position.playerToMove position)
@@ -409,6 +429,9 @@ wouldMoveLeavePlayerInCheck player position ply =
409429
isPlayerInCheck player pos
410430

411431

432+
{-| Get all of the plies that could be chosen by the current player that would not
433+
leave them in check.
434+
-}
412435
getPossibleMovesForCurrentPlayerWithoutCheck : Position -> Square -> EverySet Ply
413436
getPossibleMovesForCurrentPlayerWithoutCheck position square =
414437
getPossibleMoves True position.playerToMove position square
@@ -750,13 +773,7 @@ getRows board =
750773
{- TODO rename this -}
751774
let
752775
maybeToBool r =
753-
--TODO i'm sure there's a smoother way to do this
754-
case r of
755-
Just _ ->
756-
True
757-
758-
Nothing ->
759-
False
776+
r /= Nothing
760777
in
761778
List.range 0 (Array2D.rows board)
762779
|> List.map (\i -> Array2D.getRow i board)
@@ -774,6 +791,50 @@ canPieceMoveBetweenSquares position start end =
774791
|> List.member end
775792

776793

794+
{-| Represent the move history as a string. The moves will be represented as they would be in a
795+
Portable Game Notation (PGN) file.
796+
-}
797+
toPgn : Position -> String
798+
toPgn position =
799+
List.foldl toPgnHelp ( initial, [] ) (History.toList position.history)
800+
|> Tuple.second
801+
|> List.reverse
802+
|> String.join " "
803+
804+
805+
toPgnHelp : Ply -> ( Position, List String ) -> ( Position, List String )
806+
toPgnHelp ply ( position, strings ) =
807+
let
808+
plyText =
809+
plyToString position ply
810+
811+
nextPosition =
812+
makeMove position ply
813+
|> Maybe.withDefault initial
814+
815+
moveNumber =
816+
if position.playerToMove == Player.White then
817+
History.moveNumber nextPosition.history
818+
|> String.fromInt
819+
|> (\i -> i ++ ". ")
820+
821+
else
822+
""
823+
in
824+
( nextPosition, (moveNumber ++ plyText) :: strings )
825+
826+
827+
{-| Turn a ply into a string. This is surprisingly complicated because we want to display as little information as
828+
possible to unambiguously refer to it.
829+
830+
Suppose we have a knight moving to the f3 square:
831+
832+
- if there's only one knight that can move there, we want to output "Nf3"
833+
- if there's another Knight that can move to f3, we want to add either the file or the rank to disambiguate
834+
For example, "Naf3" or "N1f3"
835+
- if we've promoted some pawns to Knights, we might have to specify both the rank and the file: "Na1f3"
836+
837+
-}
777838
plyToString : Position -> Ply -> String
778839
plyToString position ply =
779840
case ply of
@@ -801,7 +862,7 @@ plyToString position ply =
801862
context =
802863
case casesToDisambiguate of
803864
[] ->
804-
"" |> Debug.log "shouldnt happen"
865+
"" |> Debug.todo "shouldnt happen"
805866

806867
[ _ ] ->
807868
""
@@ -848,36 +909,15 @@ plyToString position ply =
848909
"O-O"
849910

850911

851-
toPgnHelp : Ply -> ( Position, List String ) -> ( Position, List String )
852-
toPgnHelp ply ( position, strings ) =
853-
let
854-
plyText =
855-
plyToString position ply
856-
857-
nextPosition =
858-
makeMove position ply
859-
|> Maybe.withDefault initial
860-
861-
moveNumber =
862-
if position.playerToMove == Player.White then
863-
History.moveNumber nextPosition.history
864-
|> String.fromInt
865-
|> (\i -> i ++ ". ")
866-
867-
else
868-
""
869-
in
870-
( nextPosition, (moveNumber ++ plyText) :: strings )
871912

872-
873-
toPgn : Position -> String
874-
toPgn position =
875-
List.foldl toPgnHelp ( initial, [] ) (History.toList position.history)
876-
|> Tuple.second
877-
|> List.reverse
878-
|> String.join " "
913+
-- AI Functions
879914

880915

916+
{-| Pick a ply for the current player and apply it. Plies are ranked based on a bunch of heuristics that assign
917+
a score to each one, and one is picked by the seed.
918+
One day, the seed may be used for randomly selecting moves, but for now it serves like an index.
919+
If the seed is 0, the best move will always be picked.
920+
-}
881921
aiMove : Position -> Int -> Position
882922
aiMove position seed =
883923
let

0 commit comments

Comments
 (0)