-module(fen). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([parse_board/1, to_board/1, from_string/1, to_string/1]). -export_type([castling_status/0, fen/0]). -type castling_status() :: {castling_status, boolean(), boolean(), boolean(), boolean()}. -type fen() :: {fen, board:board_bb(), color:color(), castling_status(), gleam@option:option(position:position()), integer(), integer()}. -spec piece_to_fen_string(piece:piece()) -> binary(). piece_to_fen_string(Piece) -> case Piece of {piece, white, king} -> <<"K"/utf8>>; {piece, white, queen} -> <<"Q"/utf8>>; {piece, white, rook} -> <<"R"/utf8>>; {piece, white, bishop} -> <<"B"/utf8>>; {piece, white, knight} -> <<"N"/utf8>>; {piece, white, pawn} -> <<"P"/utf8>>; {piece, black, king} -> <<"k"/utf8>>; {piece, black, queen} -> <<"q"/utf8>>; {piece, black, rook} -> <<"r"/utf8>>; {piece, black, bishop} -> <<"b"/utf8>>; {piece, black, knight} -> <<"n"/utf8>>; {piece, black, pawn} -> <<"p"/utf8>> end. -spec turn_to_fen_string(color:color()) -> binary(). turn_to_fen_string(Turn) -> case Turn of white -> <<"w"/utf8>>; black -> <<"b"/utf8>> end. -spec castling_to_fen_string(castling_status()) -> binary(). castling_to_fen_string(Castling) -> White_kingside_castling = case erlang:element(2, Castling) of true -> <<"K"/utf8>>; false -> <<""/utf8>> end, White_queenside_castling = case erlang:element(3, Castling) of true -> <<"Q"/utf8>>; false -> <<""/utf8>> end, Black_kingside_castling = case erlang:element(4, Castling) of true -> <<"k"/utf8>>; false -> <<""/utf8>> end, Black_queenside_castling = case erlang:element(5, Castling) of true -> <<"q"/utf8>>; false -> <<""/utf8>> end, Castling_string = gleam@string:join( [White_kingside_castling, White_queenside_castling, Black_kingside_castling, Black_queenside_castling], <<""/utf8>> ), case Castling_string of <<""/utf8>> -> <<"-"/utf8>>; _ -> Castling_string end. -spec en_passant_to_fen_string(gleam@option:option(position:position())) -> binary(). en_passant_to_fen_string(En_passant) -> case En_passant of none -> <<"-"/utf8>>; {some, Pos} -> File_string = position:file_to_string(erlang:element(2, Pos)), Rank_string = position:rank_to_string(erlang:element(3, Pos)), gleam@string:join([File_string, Rank_string], <<""/utf8>>) end. -spec expand_rank(list(binary())) -> list(binary()). expand_rank(Rank) -> Accumulator = [], gleam@list:fold(Rank, Accumulator, fun(Acc, Part) -> case Part of <<"1"/utf8>> -> gleam@list:append(Acc, [<<""/utf8>>]); <<"2"/utf8>> -> gleam@list:append(Acc, [<<""/utf8>>, <<""/utf8>>]); <<"3"/utf8>> -> gleam@list:append( Acc, [<<""/utf8>>, <<""/utf8>>, <<""/utf8>>] ); <<"4"/utf8>> -> gleam@list:append( Acc, [<<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>] ); <<"5"/utf8>> -> gleam@list:append( Acc, [<<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>] ); <<"6"/utf8>> -> gleam@list:append( Acc, [<<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>] ); <<"7"/utf8>> -> gleam@list:append( Acc, [<<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>] ); <<"8"/utf8>> -> gleam@list:append( Acc, [<<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>] ); <<"K"/utf8>> -> gleam@list:append(Acc, [<<"K"/utf8>>]); <<"Q"/utf8>> -> gleam@list:append(Acc, [<<"Q"/utf8>>]); <<"R"/utf8>> -> gleam@list:append(Acc, [<<"R"/utf8>>]); <<"B"/utf8>> -> gleam@list:append(Acc, [<<"B"/utf8>>]); <<"N"/utf8>> -> gleam@list:append(Acc, [<<"N"/utf8>>]); <<"P"/utf8>> -> gleam@list:append(Acc, [<<"P"/utf8>>]); <<"k"/utf8>> -> gleam@list:append(Acc, [<<"k"/utf8>>]); <<"q"/utf8>> -> gleam@list:append(Acc, [<<"q"/utf8>>]); <<"r"/utf8>> -> gleam@list:append(Acc, [<<"r"/utf8>>]); <<"b"/utf8>> -> gleam@list:append(Acc, [<<"b"/utf8>>]); <<"n"/utf8>> -> gleam@list:append(Acc, [<<"n"/utf8>>]); <<"p"/utf8>> -> gleam@list:append(Acc, [<<"p"/utf8>>]); _ -> gleam@list:append(Acc, [Part]) end end). -spec parse_board(binary()) -> board:board_bb(). parse_board(Board_string) -> List_of_ranks_as_strings = gleam@string:split(Board_string, <<"/"/utf8>>), Accumulator = {board_bb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, gleam@list:index_fold( List_of_ranks_as_strings, Accumulator, fun(Acc, Rank_as_string, Rank_index) -> Rank_index@1 = position:int_to_rank(7 - Rank_index), Rank_parts = gleam@string:to_graphemes(Rank_as_string), Expanded_rank = expand_rank(Rank_parts), gleam@list:index_fold( Expanded_rank, Acc, fun(Acc@1, Square, File_index) -> File_index@1 = position:int_to_file(File_index), case Square of <<""/utf8>> -> Acc@1; <<"K"/utf8>> -> New_white_king_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), bitboard:'or'( erlang:element(8, Acc@1), New_white_king_bitboard ), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"Q"/utf8>> -> New_white_queen_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), bitboard:'or'( erlang:element(9, Acc@1), New_white_queen_bitboard ), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"R"/utf8>> -> New_white_rook_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), bitboard:'or'( erlang:element(10, Acc@1), New_white_rook_bitboard ), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"B"/utf8>> -> New_white_bishop_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), bitboard:'or'( erlang:element(11, Acc@1), New_white_bishop_bitboard ), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"N"/utf8>> -> New_white_knight_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), bitboard:'or'( erlang:element(12, Acc@1), New_white_knight_bitboard ), erlang:element(13, Acc@1)}; <<"P"/utf8>> -> New_white_pawn_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), bitboard:'or'( erlang:element(13, Acc@1), New_white_pawn_bitboard )}; <<"k"/utf8>> -> New_black_king_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, bitboard:'or'( erlang:element(2, Acc@1), New_black_king_bitboard ), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"q"/utf8>> -> New_black_queen_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), bitboard:'or'( erlang:element(3, Acc@1), New_black_queen_bitboard ), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"r"/utf8>> -> New_black_rook_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), bitboard:'or'( erlang:element(4, Acc@1), New_black_rook_bitboard ), erlang:element(5, Acc@1), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"b"/utf8>> -> New_black_bishop_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), bitboard:'or'( erlang:element(5, Acc@1), New_black_bishop_bitboard ), erlang:element(6, Acc@1), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"n"/utf8>> -> New_black_knight_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), bitboard:'or'( erlang:element(6, Acc@1), New_black_knight_bitboard ), erlang:element(7, Acc@1), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; <<"p"/utf8>> -> New_black_pawns_bitboard = board:from_position( {position, File_index@1, Rank_index@1} ), {board_bb, erlang:element(2, Acc@1), erlang:element(3, Acc@1), erlang:element(4, Acc@1), erlang:element(5, Acc@1), erlang:element(6, Acc@1), bitboard:'or'( erlang:element(7, Acc@1), New_black_pawns_bitboard ), erlang:element(8, Acc@1), erlang:element(9, Acc@1), erlang:element(10, Acc@1), erlang:element(11, Acc@1), erlang:element(12, Acc@1), erlang:element(13, Acc@1)}; _ -> erlang:error(#{gleam_error => panic, message => <<"panic expression evaluated"/utf8>>, module => <<"fen"/utf8>>, function => <<"parse_board"/utf8>>, line => 621}) end end ) end ). -spec to_board(binary()) -> board:board_bb(). to_board(Fen) -> Fen_string_parts = gleam@string:split(Fen, <<" "/utf8>>), Parsed_board@1 = case gleam@list:length(Fen_string_parts) =:= 6 of false -> erlang:error(#{gleam_error => panic, message => <<"Invalid FEN string"/utf8>>, module => <<"fen"/utf8>>, function => <<"to_board"/utf8>>, line => 107}); true -> [Board_string | _] = case Fen_string_parts of [_ | _] -> Fen_string_parts; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"fen"/utf8>>, function => <<"to_board"/utf8>>, line => 109}) end, Parsed_board = parse_board(Board_string), Parsed_board end, Parsed_board@1. -spec parse_turn(binary()) -> color:color(). parse_turn(Turn_string) -> case Turn_string of <<"w"/utf8>> -> white; <<"b"/utf8>> -> black; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid turn string, must be 'w' or 'b'"/utf8>>, module => <<"fen"/utf8>>, function => <<"parse_turn"/utf8>>, line => 662}) end. -spec parse_castling(binary()) -> castling_status(). parse_castling(Castling_string) -> case gleam@string:length(Castling_string) =< 4 of true -> Cs_parts = gleam@string:split(Castling_string, <<""/utf8>>), gleam@list:each(Cs_parts, fun(Part) -> case Part of <<"K"/utf8>> -> nil; <<"Q"/utf8>> -> nil; <<"k"/utf8>> -> nil; <<"q"/utf8>> -> nil; <<"-"/utf8>> -> nil; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid castling string"/utf8>>, module => <<"fen"/utf8>>, function => <<"parse_castling"/utf8>>, line => 677}) end end), White_queenside_castling = gleam_stdlib:contains_string( Castling_string, <<"Q"/utf8>> ), White_kingside_castling = gleam_stdlib:contains_string( Castling_string, <<"K"/utf8>> ), Black_queenside_castling = gleam_stdlib:contains_string( Castling_string, <<"q"/utf8>> ), Black_kingside_castling = gleam_stdlib:contains_string( Castling_string, <<"k"/utf8>> ), {castling_status, White_kingside_castling, White_queenside_castling, Black_kingside_castling, Black_queenside_castling}; false -> erlang:error(#{gleam_error => panic, message => <<"Invalid castling string"/utf8>>, module => <<"fen"/utf8>>, function => <<"parse_castling"/utf8>>, line => 692}) end. -spec parse_file(binary()) -> position:file(). parse_file(File_string) -> case File_string of <<"a"/utf8>> -> a; <<"b"/utf8>> -> b; <<"c"/utf8>> -> c; <<"d"/utf8>> -> d; <<"e"/utf8>> -> e; <<"f"/utf8>> -> f; <<"g"/utf8>> -> g; <<"h"/utf8>> -> h; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid file string"/utf8>>, module => <<"fen"/utf8>>, function => <<"parse_file"/utf8>>, line => 723}) end. -spec parse_rank(binary()) -> position:rank(). parse_rank(Rank_string) -> case Rank_string of <<"1"/utf8>> -> one; <<"2"/utf8>> -> two; <<"3"/utf8>> -> three; <<"4"/utf8>> -> four; <<"5"/utf8>> -> five; <<"6"/utf8>> -> six; <<"7"/utf8>> -> seven; <<"8"/utf8>> -> eight; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid rank string"/utf8>>, module => <<"fen"/utf8>>, function => <<"parse_rank"/utf8>>, line => 737}) end. -spec parse_en_passant(binary()) -> gleam@option:option(position:position()). parse_en_passant(En_passant_string) -> case En_passant_string of <<"-"/utf8>> -> none; _ -> case gleam@string:length(En_passant_string) =:= 2 of true -> nil; false -> erlang:error(#{gleam_error => panic, message => <<"Invalid en passant string"/utf8>>, module => <<"fen"/utf8>>, function => <<"parse_en_passant"/utf8>>, line => 702}) end, En_passant_parts = gleam@string:split( En_passant_string, <<""/utf8>> ), [File_string, Rank_string] = case En_passant_parts of [_, _] -> En_passant_parts; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"fen"/utf8>>, function => <<"parse_en_passant"/utf8>>, line => 705}) end, File = parse_file(File_string), Rank = parse_rank(Rank_string), {some, {position, File, Rank}} end. -spec string_to_int(binary()) -> integer(). string_to_int(String) -> case gleam@string:length(String) of 1 -> case String of <<"0"/utf8>> -> 0; <<"1"/utf8>> -> 1; <<"2"/utf8>> -> 2; <<"3"/utf8>> -> 3; <<"4"/utf8>> -> 4; <<"5"/utf8>> -> 5; <<"6"/utf8>> -> 6; <<"7"/utf8>> -> 7; <<"8"/utf8>> -> 8; <<"9"/utf8>> -> 9; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid halfmove string"/utf8>>, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 763}) end; 2 -> _assert_subject = gleam@string:split(String, <<""/utf8>>), [Tenths_place_string, Ones_place_string] = case _assert_subject of [_, _] -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 767}) end, Tenths_place = case Tenths_place_string of <<"0"/utf8>> -> 0; <<"1"/utf8>> -> 10; <<"2"/utf8>> -> 20; <<"3"/utf8>> -> 30; <<"4"/utf8>> -> 40; <<"5"/utf8>> -> 50; <<"6"/utf8>> -> 60; <<"7"/utf8>> -> 70; <<"8"/utf8>> -> 80; <<"9"/utf8>> -> 90; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid halfmove string"/utf8>>, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 780}) end, Ones_place = case Ones_place_string of <<"0"/utf8>> -> 0; <<"1"/utf8>> -> 1; <<"2"/utf8>> -> 2; <<"3"/utf8>> -> 3; <<"4"/utf8>> -> 4; <<"5"/utf8>> -> 5; <<"6"/utf8>> -> 6; <<"7"/utf8>> -> 7; <<"8"/utf8>> -> 8; <<"9"/utf8>> -> 9; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid halfmove string"/utf8>>, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 794}) end, Tenths_place + Ones_place; 3 -> _assert_subject@1 = gleam@string:split(String, <<""/utf8>>), [Hundreds_place_string, Tenths_place_string@1, Ones_place_string@1] = case _assert_subject@1 of [_, _, _] -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 799}) end, Hundreds_place = case Hundreds_place_string of <<"0"/utf8>> -> 0; <<"1"/utf8>> -> 100; <<"2"/utf8>> -> 200; <<"3"/utf8>> -> 300; <<"4"/utf8>> -> 400; <<"5"/utf8>> -> 500; <<"6"/utf8>> -> 600; <<"7"/utf8>> -> 700; <<"8"/utf8>> -> 800; <<"9"/utf8>> -> 900; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid halfmove string"/utf8>>, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 812}) end, Tenths_place@1 = case Tenths_place_string@1 of <<"0"/utf8>> -> 0; <<"1"/utf8>> -> 10; <<"2"/utf8>> -> 20; <<"3"/utf8>> -> 30; <<"4"/utf8>> -> 40; <<"5"/utf8>> -> 50; <<"6"/utf8>> -> 60; <<"7"/utf8>> -> 70; <<"8"/utf8>> -> 80; <<"9"/utf8>> -> 90; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid halfmove string"/utf8>>, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 826}) end, Ones_place@1 = case Ones_place_string@1 of <<"0"/utf8>> -> 0; <<"1"/utf8>> -> 1; <<"2"/utf8>> -> 2; <<"3"/utf8>> -> 3; <<"4"/utf8>> -> 4; <<"5"/utf8>> -> 5; <<"6"/utf8>> -> 6; <<"7"/utf8>> -> 7; <<"8"/utf8>> -> 8; <<"9"/utf8>> -> 9; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid halfmove string"/utf8>>, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 840}) end, (Hundreds_place + Tenths_place@1) + Ones_place@1; _ -> erlang:error(#{gleam_error => panic, message => <<"Invalid halfmove string"/utf8>>, module => <<"fen"/utf8>>, function => <<"string_to_int"/utf8>>, line => 844}) end. -spec parse_halfmove(binary()) -> integer(). parse_halfmove(Halfmove_string) -> string_to_int(Halfmove_string). -spec parse_fullmove(binary()) -> integer(). parse_fullmove(Fullmove_string) -> string_to_int(Fullmove_string). -spec from_string(binary()) -> fen(). from_string(Fen) -> Fen_string_parts = gleam@string:split(Fen, <<" "/utf8>>), case gleam@list:length(Fen_string_parts) =:= 6 of false -> erlang:error(#{gleam_error => panic, message => <<"Invalid FEN string"/utf8>>, module => <<"fen"/utf8>>, function => <<"from_string"/utf8>>, line => 266}); true -> [Board_string | Rest] = case Fen_string_parts of [_ | _] -> Fen_string_parts; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"fen"/utf8>>, function => <<"from_string"/utf8>>, line => 268}) end, [Turn_string | Rest@1] = case Rest of [_ | _] -> Rest; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"fen"/utf8>>, function => <<"from_string"/utf8>>, line => 269}) end, [Castling_string | Rest@2] = case Rest@1 of [_ | _] -> Rest@1; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@2, module => <<"fen"/utf8>>, function => <<"from_string"/utf8>>, line => 270}) end, [En_passant_string | Rest@3] = case Rest@2 of [_ | _] -> Rest@2; _assert_fail@3 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@3, module => <<"fen"/utf8>>, function => <<"from_string"/utf8>>, line => 271}) end, [Halfmove_string | Rest@4] = case Rest@3 of [_ | _] -> Rest@3; _assert_fail@4 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@4, module => <<"fen"/utf8>>, function => <<"from_string"/utf8>>, line => 272}) end, [Fullmove_string | _] = case Rest@4 of [_ | _] -> Rest@4; _assert_fail@5 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@5, module => <<"fen"/utf8>>, function => <<"from_string"/utf8>>, line => 273}) end, Parsed_board = parse_board(Board_string), Parsed_turn = parse_turn(Turn_string), Parsed_castling = parse_castling(Castling_string), Parsed_en_passant = parse_en_passant(En_passant_string), Parsed_halfmove = parse_halfmove(Halfmove_string), Parsed_fullmove = parse_fullmove(Fullmove_string), Fen@1 = {fen, Parsed_board, Parsed_turn, Parsed_castling, Parsed_en_passant, Parsed_halfmove, Parsed_fullmove}, Fen@1 end. -spec board_to_fen_string(board:board_bb()) -> binary(). board_to_fen_string(Board) -> Board_string = gleam@list:fold( [{position, a, eight}, {position, b, eight}, {position, c, eight}, {position, d, eight}, {position, e, eight}, {position, f, eight}, {position, g, eight}, {position, h, eight}, {position, a, seven}, {position, b, seven}, {position, c, seven}, {position, d, seven}, {position, e, seven}, {position, f, seven}, {position, g, seven}, {position, h, seven}, {position, a, six}, {position, b, six}, {position, c, six}, {position, d, six}, {position, e, six}, {position, f, six}, {position, g, six}, {position, h, six}, {position, a, five}, {position, b, five}, {position, c, five}, {position, d, five}, {position, e, five}, {position, f, five}, {position, g, five}, {position, h, five}, {position, a, four}, {position, b, four}, {position, c, four}, {position, d, four}, {position, e, four}, {position, f, four}, {position, g, four}, {position, h, four}, {position, a, three}, {position, b, three}, {position, c, three}, {position, d, three}, {position, e, three}, {position, f, three}, {position, g, three}, {position, h, three}, {position, a, two}, {position, b, two}, {position, c, two}, {position, d, two}, {position, e, two}, {position, f, two}, {position, g, two}, {position, h, two}, {position, a, one}, {position, b, one}, {position, c, one}, {position, d, one}, {position, e, one}, {position, f, one}, {position, g, one}, {position, h, one}], <<""/utf8>>, fun(Acc, Pos) -> Piece = board:get_piece_at_position(Board, Pos), Acc@1 = case Piece of none -> Last_char = gleam@string:last(Acc), case Last_char of {error, nil} -> gleam@string:append(Acc, <<"1"/utf8>>); {ok, <<"/"/utf8>>} -> gleam@string:append(Acc, <<"1"/utf8>>); {ok, <<"1"/utf8>>} -> gleam@string:append( gleam@string:drop_right(Acc, 1), <<"2"/utf8>> ); {ok, <<"2"/utf8>>} -> gleam@string:append( gleam@string:drop_right(Acc, 1), <<"3"/utf8>> ); {ok, <<"3"/utf8>>} -> gleam@string:append( gleam@string:drop_right(Acc, 1), <<"4"/utf8>> ); {ok, <<"4"/utf8>>} -> gleam@string:append( gleam@string:drop_right(Acc, 1), <<"5"/utf8>> ); {ok, <<"5"/utf8>>} -> gleam@string:append( gleam@string:drop_right(Acc, 1), <<"6"/utf8>> ); {ok, <<"6"/utf8>>} -> gleam@string:append( gleam@string:drop_right(Acc, 1), <<"7"/utf8>> ); {ok, <<"7"/utf8>>} -> gleam@string:append( gleam@string:drop_right(Acc, 1), <<"8"/utf8>> ); {ok, <<"8"/utf8>>} -> erlang:error(#{gleam_error => panic, message => <<"Unable to encode BoardBB to FEN string"/utf8>>, module => <<"fen"/utf8>>, function => <<"board_to_fen_string"/utf8>>, line => 147}); {ok, _} -> gleam@string:append(Acc, <<"1"/utf8>>) end; {some, Piece@1} -> Piece_string = piece_to_fen_string(Piece@1), gleam@string:append(Acc, Piece_string) end, Acc@2 = case Pos of {position, h, one} -> Acc@1; {position, h, _} -> gleam@string:append(Acc@1, <<"/"/utf8>>); _ -> Acc@1 end, Acc@2 end ), Board_string. -spec to_string(fen()) -> binary(). to_string(Fen) -> Board_string = board_to_fen_string(erlang:element(2, Fen)), Turn_string = turn_to_fen_string(erlang:element(3, Fen)), Castling_string = castling_to_fen_string(erlang:element(4, Fen)), En_passant_string = en_passant_to_fen_string(erlang:element(5, Fen)), Halfmove_string = gleam@int:to_string(erlang:element(6, Fen)), Fullmove_string = gleam@int:to_string(erlang:element(7, Fen)), Fen_string = gleam@string:join( [Board_string, Turn_string, Castling_string, En_passant_string, Halfmove_string, Fullmove_string], <<" "/utf8>> ), Fen_string.