Current section

Files

Jump to
binbo src binbo_movegen.erl
Raw

src/binbo_movegen.erl

%% Copyright (c) 2019, Sergei Semichev <chessvegas@chessvegas.com>. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(binbo_movegen).
-export([all_valid_moves/2, all_valid_moves/3]).
-export([has_valid_moves/2]).
%%%------------------------------------------------------------------------------
%%% Includes
%%%------------------------------------------------------------------------------
-include("binbo_board.hrl").
%%%------------------------------------------------------------------------------
%%% Types
%%%------------------------------------------------------------------------------
-type bb() :: binbo_bb:bb().
-type piece() :: binbo_board:piece().
-type piece_type() :: binbo_board:piece_type().
-type color() :: binbo_board:color().
-type bb_game() :: binbo_position:bb_game().
-type sq_idx() :: binbo_board:square_index().
-type sq_notation() :: binbo_board:square_notation().
-type many() :: all | any.
-type bb_map() :: #{sq_idx() => {piece(), bb()}}.
-type promo_to() :: 'q' | 'r' | 'b' | 'n'.
-type int_move() :: {sq_idx(), sq_idx()} | {sq_idx(), sq_idx(), promo_to()}.
-type bin_move() :: {sq_notation(), sq_notation()} | {sq_notation(), sq_notation(), promo_to()}.
-type str_move() :: {string(), string()} | {string(), string(), promo_to()}.
-export_type([int_move/0, bin_move/0, str_move/0]).
%%%------------------------------------------------------------------------------
%%% API
%%%------------------------------------------------------------------------------
%% all_valid_moves/2
-spec all_valid_moves(bb_game(), int) -> [int_move()]
; (bb_game(), bin) -> [bin_move()]
; (bb_game(), str) -> [str_move()].
all_valid_moves(Game, MoveType) ->
Color = binbo_position:get_sidetomove(Game),
all_valid_moves(Color, Game, MoveType).
%% all_valid_moves/3
-spec all_valid_moves(color(), bb_game(), int) -> [int_move()]
; (color(), bb_game(), bin) -> [bin_move()]
; (color(), bb_game(), str) -> [str_move()].
all_valid_moves(Color, Game, MoveType) ->
Map = valid_moves(Color, Game, all, map),
bbmap_to_movelist(Map, MoveType).
%% has_valid_moves/2
-spec has_valid_moves(color(), bb_game()) -> boolean().
has_valid_moves(Color, Game) ->
(any_valid_move(Color, Game) > ?EMPTY_BB).
%%%------------------------------------------------------------------------------
%%% Internal functions
%%%------------------------------------------------------------------------------
%% any_valid_move/2
-spec any_valid_move(color(), bb_game()) -> bb().
any_valid_move(Color, Game) ->
valid_moves(Color, Game, any, bb).
%% valid_moves/4
-spec valid_moves(color(), bb_game(), many(), bb) -> bb()
; (color(), bb_game(), many(), map) -> bb_map().
valid_moves(Color, Game, Many, DataType) ->
FromSquares = binbo_position:get_side_indexes(Color, Game),
valid_moves_from(FromSquares, Game, Many, DataType).
%% valid_moves_from/4
-spec valid_moves_from([sq_idx()], bb_game(), many(), bb) -> bb()
; ([sq_idx()], bb_game(), many(), map) -> bb_map().
valid_moves_from(FromSquares, Game, Many, DataType) ->
case DataType of
bb -> acc_valid_moves_from(FromSquares, Game, Many, ?EMPTY_BB);
map -> acc_valid_moves_from(FromSquares, Game, Many, #{})
end.
%% acc_valid_moves_from/4
-spec acc_valid_moves_from([sq_idx()], bb_game(), many(), bb()) -> bb()
; ([sq_idx()], bb_game(), many(), map()) -> bb_map().
acc_valid_moves_from([], _Game, _Many, MovesAcc) ->
MovesAcc;
acc_valid_moves_from([FromIdx | Tail], Game, Many, MovesAcc) ->
Piece = binbo_position:get_piece(FromIdx, Game),
true = ?IS_PIECE(Piece), % ensure piece
PieceMovesBB = valid_piece_moves(FromIdx, Piece, Game, Many),
case PieceMovesBB > ?EMPTY_BB of
true when (Many =:= any) ->
PieceMovesBB;
true when is_integer(MovesAcc) ->
acc_valid_moves_from(Tail, Game, Many, MovesAcc bor PieceMovesBB);
true when is_map(MovesAcc) ->
acc_valid_moves_from(Tail, Game, Many, MovesAcc#{FromIdx => {Piece, PieceMovesBB}});
false ->
acc_valid_moves_from(Tail, Game, Many, MovesAcc)
end.
%% valid_piece_moves/4
-spec valid_piece_moves(sq_idx(), piece(), bb_game(), many()) -> bb().
valid_piece_moves(FromIdx, Piece, Game, Many) ->
PieceMovesBB = position_piece_moves_bb(FromIdx, Piece, Game),
ToSquares = binbo_bb:to_index_list(PieceMovesBB),
valid_piece_moves_from_to(FromIdx, ToSquares, Piece, Game, Many, ?EMPTY_BB).
%% valid_piece_moves_from_to/6
-spec valid_piece_moves_from_to(sq_idx(), [sq_idx()], piece(), bb_game(), many(), bb()) -> bb().
valid_piece_moves_from_to(_FromIdx, [], _Piece, _Game, _Many, MovesBB) ->
MovesBB;
valid_piece_moves_from_to(FromIdx, [ToIdx | Tail], Piece, Game, Many, MovesBB) ->
Validate = binbo_move:validate_move(Game, Piece, FromIdx, ToIdx, ?QUEEN),
case Validate of
{ok, _, _} ->
case Many of
any ->
?SQUARE_BB(ToIdx);
all ->
MovesBB2 = MovesBB bor ?SQUARE_BB(ToIdx),
valid_piece_moves_from_to(FromIdx, Tail, Piece, Game, Many, MovesBB2)
end;
{error, _} ->
valid_piece_moves_from_to(FromIdx, Tail, Piece, Game, Many, MovesBB)
end.
%% position_piece_moves_bb/3
-spec position_piece_moves_bb(sq_idx(), piece(), bb_game()) -> bb().
position_piece_moves_bb(FromIdx, Piece, Game) ->
Ptype = ?PIECE_TYPE(Piece),
Pcolor = ?COLOR(Piece),
binbo_position:piece_moves_bb(FromIdx, Ptype, Pcolor, Game).
%% bbmap_to_movelist/1
-spec bbmap_to_movelist(bb_map(), int) -> [int_move()]
; (bb_map(), bin) -> [bin_move()]
; (bb_map(), str) -> [str_move()].
bbmap_to_movelist(Map, Movetype) ->
List = maps:to_list(Map),
bblist_to_movelist(List, Movetype, []).
%% bblist_to_movelist/2
-spec bblist_to_movelist([{sq_idx(), {piece(), bb()}}], int, [int_move()]) -> [int_move()]
; ([{sq_idx(), {piece(), bb()}}], bin, [bin_move()]) -> [bin_move()]
; ([{sq_idx(), {piece(), bb()}}], str, [str_move()]) -> [str_move()].
bblist_to_movelist([], _Movetype, Movelist) ->
Movelist;
bblist_to_movelist([{FromIdx, {Piece, MovesBB}} | Tail], Movetype, Movelist) ->
IdxList = binbo_bb:to_index_list(MovesBB),
Ptype = ?PIECE_TYPE(Piece),
Movelist2 = lists:foldl(fun(ToIdx, Acc) ->
maybe_movelist_with_promotion(Ptype, FromIdx, ToIdx, Movetype, Acc)
end, Movelist, IdxList),
bblist_to_movelist(Tail, Movetype, Movelist2).
%% maybe_movelist_with_promotion/5
-spec maybe_movelist_with_promotion(piece_type(), sq_idx(), sq_idx(), int, [int_move()]) -> [int_move()]
; (piece_type(), sq_idx(), sq_idx(), bin, [bin_move()]) -> [bin_move()]
; (piece_type(), sq_idx(), sq_idx(), str, [str_move()]) -> [str_move()].
maybe_movelist_with_promotion(Ptype, FromIdx, ToIdx, Movetype, Movelist) ->
IsPromotion = case (Ptype =:= ?PAWN) of
true ->
ToBB = ?SQUARE_BB(ToIdx),
_ = ?IS_AND(ToBB, ?RANK_8_BB) orelse ?IS_AND(ToBB, ?RANK_1_BB);
false ->
false
end,
{From, To} = case Movetype of
int ->
{FromIdx, ToIdx};
bin ->
{binbo_board:index_to_notation(FromIdx), binbo_board:index_to_notation(ToIdx)};
str ->
{erlang:binary_to_list(binbo_board:index_to_notation(FromIdx)), erlang:binary_to_list(binbo_board:index_to_notation(ToIdx))}
end,
case IsPromotion of
false ->
[{From, To} | Movelist];
true ->
[{From, To, q}, {From, To, r}, {From, To, b}, {From, To, n} | Movelist]
end.