I'm implementing a Chess game (Chinese Chess, aka. Xiangqi, to be exact) in Erlang.
A piece is represented by a {Color, Type}
tuple, and a point (ie. location) is represented by a {File, Rank}
tuple. The board is represented by a point-to-piece map (ie. #{point() => piece()}
).
There is a function to query whether a particular point on the board is occupied by a piece or not:
is_point_occupied_simple(Board, Point) ->
ensure_is_point(Point),
case maps:find(Point, Board) of
{ok, _} ->
true;
error ->
false
end.
However, I would like to add an optional parameter to check the color of the piece - if the point is occupied by a piece of the specified color, the function returns true; otherwise it returns false. If I don't care about the color of the piece, I can just put '_'
in the TargetColor
parameter (or, equivalently, invoke is_point_occupied/2
):
is_point_occupied(Board, Point) ->
is_point_occupied(Board, Point, '_').
is_point_occupied(Board, Point, '_') ->
ensure_is_point(Point),
case maps:find(Point, Board) of
{ok, _} ->
true;
error ->
false
end;
is_point_occupied(Board, Point, TargetColor) ->
ensure_is_point(Point),
ensure_is_color(TargetColor),
case maps:find(Point, Board) of
{ok, {TargetColor, _}} ->
true;
{ok, _} ->
false;
error ->
false
end.
I don't like the above implementation because of the large proportion of copy-and-paste, so I simplified the above function like this:
is_point_occupied_2(Board, Point) ->
is_point_occupied_2(Board, Point, '_').
is_point_occupied_2(Board, Point, TargetColor) ->
ensure_is_point(Point),
ensure_is_color_or_wildcard(TargetColor),
case maps:find(Point, Board) of
{ok, {TargetColor, _}} ->
true;
{ok, _} ->
is_wildcard(TargetColor);
error ->
false
end.
The function is_wildcard/1
is simply a one-liner:
is_wildcard(Wildcard) -> Wildcard =:= '_'.
Now, I would like to go further to replace TargetColor
by TargetPiece
, which is a {TargetColor, TargetType}
tuple. None, one or both of the tuple elements may be a wildcard ('_'
). I found it difficult to write the case
clauses. I also notice that to match an n-tuple allowing "don't care" in this way, there needs 2n clauses. So obviously it is not the proper way to implement this.
Does anyone have better ideas?
PS: I didn't include the source of all functions since those I didn't include are trivial to implement in my opinion. Please leave a comment below if you're interested. Thanks!
I would change the chess board representation, filling the whole board by either a piece {Color,Type} or no piece {none,none} so your code can be more regular: