online multiplayer chess game (note server currently down)
Diffstat (limited to 'board/chess.gd')
-rw-r--r--board/chess.gd151
1 files changed, 95 insertions, 56 deletions
diff --git a/board/chess.gd b/board/chess.gd
index 882c56a..fe60e25 100644
--- a/board/chess.gd
+++ b/board/chess.gd
@@ -119,7 +119,7 @@ static func stripped_san(move: String) -> String:
# this func is used to uniquely identify ambiguous moves
-func get_disambiguator(move: Dictionary, moves: Array) -> String:
+static func get_disambiguator(move: Dictionary, moves: Array) -> String:
var from: int = move.from
var to: int = move.to
var piece: String = move.piece
@@ -171,6 +171,67 @@ static func infer_piece_type(san: String) -> String:
return piece_type
+# WARNING: If `localized` is disabled, you must use localize_piece_move on the returned piece object.
+func piece_moves(_square_: String, piece: String, color := turn, localize := true) -> Array:
+ var moves := []
+ var second_rank := {b = RANK_7, w = RANK_2}
+ var sq: int = SQUARE_MAP[_square_]
+ if piece == PAWN:
+ # single square, non capturing
+ var square = sq + PAWN_OFFSETS[color][0]
+ if square <= SQUARE_MAP.h1:
+ __add_piece_move(moves, sq, square, piece)
+
+ # double square
+ var _square: int = sq + PAWN_OFFSETS[color][1]
+ if second_rank[color] == rank(sq):
+ __add_piece_move(moves, sq, _square, piece, BITS.BIG_PAWN)
+
+ # pawn captures
+ for j in range(2, 4):
+ var _square: int = sq + PAWN_OFFSETS[color][j]
+ if _square & 0x88: # off the board
+ continue
+ __add_piece_move(moves, sq, _square, piece, BITS.CAPTURE)
+ else:
+ for offset in PIECE_OFFSETS[piece]:
+ var square := sq
+ while true:
+ square += offset
+ if square & 0x88:
+ break
+
+ __add_piece_move(moves, sq, square, piece)
+ if piece in KNIGHT + KING:
+ break
+
+ if piece == KING and _square_ in ["e1", "e8"]:
+ __add_piece_move(moves, sq, sq + 2, piece, BITS.KSIDE_CASTLE)
+ __add_piece_move(moves, sq, sq - 2, piece, BITS.QSIDE_CASTLE)
+
+ if localize:
+ for i in range(len(moves)):
+ moves[i] = localize_piece_move(moves[i])
+ return moves
+
+
+static func __add_piece_move(moves: Array, from: int, to: int, piece: String, flags := BITS.NORMAL) -> void:
+ var mov = {from = from, to = to, flags = flags}
+ if piece == PAWN && (rank(to) == RANK_8 || rank(to) == RANK_1):
+ for p in [QUEEN, ROOK, BISHOP, KNIGHT]:
+ var m = mov.duplicate()
+ m["promotion"] = p
+ m.flags |= BITS.PROMOTION
+ moves.append(m)
+ else:
+ moves.append(mov)
+
+
+# Please only use the returned object if the team the move was created for is the current turn.
+func localize_piece_move(piece_move: Dictionary) -> Dictionary:
+ return __build_move(piece_move.from, piece_move.to, piece_move.flags, piece_move.get("promotion", ""))
+
+
###
### utility functions
###
@@ -182,6 +243,10 @@ static func file(i: int) -> int:
return i & 15
+static func vecfrom0x88(i: int) -> Vector2:
+ return Vector2(file(i), rank(i))
+
+
static func vec2algebraic(pos: Vector2) -> String:
var column := "abcdefgh"[pos.x]
var row := str(round(8 - pos.y))
@@ -206,7 +271,7 @@ static func offset(pos, offset: Vector2) -> String:
if typeof(pos) == TYPE_STRING: # algbraic
return vec2algebraic(algebraic2vec(pos) + offset)
elif typeof(pos) == TYPE_INT: # board pos
- return offset(algebraic(pos), offset) # supreme lazy
+ return vec2algebraic(vecfrom0x88(pos) + offset)
return ""
@@ -357,38 +422,35 @@ func remove(square) -> Dictionary:
return piece
-# warning-ignore:shadowed_variable
-func __build_move(board: Array, from: int, to: int, flags: int, promotion := ""):
+func __add_move(moves: Array, from: int, to: int, flags := BITS.NORMAL, b := board) -> void:
+ # if pawn promotion
+ if b[from].type == PAWN && (rank(to) == RANK_8 || rank(to) == RANK_1):
+ for p in [QUEEN, ROOK, BISHOP, KNIGHT]:
+ moves.append(__build_move(from, to, flags, p))
+ else:
+ moves.append(__build_move(from, to, flags))
+
+
+func __build_move(from: int, to: int, flags: int = BITS.NORMAL, promotion := "", _board: Array = board):
var move := {
color = turn,
from = from,
to = to,
flags = flags,
- piece = board[from].type,
+ piece = _board[from].type,
}
if promotion:
move.flags |= BITS.PROMOTION
move.promotion = promotion
- if board[to]:
- move.captured = board[to].type
+ if _board[to]:
+ move.captured = _board[to].type
elif flags & BITS.EP_CAPTURE:
move.captured = PAWN
return move
-# warning-ignore:shadowed_variable
-func __add_move(board: Array, moves: Array, from: int, to: int, flags: int) -> void:
- # if pawn promotion
- if board[from].type == PAWN && (rank(to) == RANK_8 || rank(to) == RANK_1):
- var pieces := [QUEEN, ROOK, BISHOP, KNIGHT]
- for p in pieces:
- moves.append(__build_move(board, from, to, flags, p))
- else:
- moves.append(__build_move(board, from, to, flags))
-
-
func __generate_moves(options := {}) -> Array:
var moves := []
var us := turn
@@ -430,12 +492,12 @@ func __generate_moves(options := {}) -> Array:
# single square, non capturing
var square = i + PAWN_OFFSETS[us][0]
if square <= SQUARE_MAP.h1 and board[square] == null:
- __add_move(board, moves, i, square, BITS.NORMAL)
+ __add_move(moves, i, square, BITS.NORMAL)
# double square
var _square: int = i + PAWN_OFFSETS[us][1]
if second_rank[us] == rank(i) && board[_square] == null:
- __add_move(board, moves, i, _square, BITS.BIG_PAWN)
+ __add_move(moves, i, _square, BITS.BIG_PAWN)
# pawn captures
for j in range(2, 4):
@@ -444,9 +506,9 @@ func __generate_moves(options := {}) -> Array:
continue
if board[_square] != null && board[_square].color == them:
- __add_move(board, moves, i, _square, BITS.CAPTURE)
+ __add_move(moves, i, _square, BITS.CAPTURE)
elif _square == ep_square:
- __add_move(board, moves, i, ep_square, BITS.EP_CAPTURE)
+ __add_move(moves, i, ep_square, BITS.EP_CAPTURE)
elif piece_type == "-1" || piece_type == piece.type:
for offset in PIECE_OFFSETS[piece.type]:
var square := i
@@ -457,11 +519,11 @@ func __generate_moves(options := {}) -> Array:
break
if board[square] == null:
- __add_move(board, moves, i, square, BITS.NORMAL)
+ __add_move(moves, i, square, BITS.NORMAL)
else:
if board[square].color == us:
break
- __add_move(board, moves, i, square, BITS.CAPTURE)
+ __add_move(moves, i, square, BITS.CAPTURE)
break
# break, if knight or king
@@ -484,7 +546,7 @@ func __generate_moves(options := {}) -> Array:
&& !__attacked(them, castling_from + 1)
&& !__attacked(them, castling_to)
):
- __add_move(board, moves, kings[us], castling_to, BITS.KSIDE_CASTLE)
+ __add_move(moves, kings[us], castling_to, BITS.KSIDE_CASTLE)
# queen-side castling
if castling[us] & BITS.QSIDE_CASTLE:
@@ -499,7 +561,7 @@ func __generate_moves(options := {}) -> Array:
&& !__attacked(them, castling_from - 1)
&& !__attacked(them, castling_to)
):
- __add_move(board, moves, kings[us], castling_to, BITS.QSIDE_CASTLE)
+ __add_move(moves, kings[us], castling_to, BITS.QSIDE_CASTLE)
# return all pseudo-legal moves (this includes moves that allow the king
# to be captured)
@@ -714,10 +776,7 @@ func __make_move(move: Dictionary):
# if ep capture, remove the captured pawn
if move.flags & BITS.EP_CAPTURE:
- if turn == BLACK:
- board[move.to - 16] = null
- else:
- board[move.to + 16] = null
+ board[move.to + (-16 if us == BLACK else 16)] = null
# if pawn promotion, replace with new piece
if move.flags & BITS.PROMOTION:
@@ -756,24 +815,12 @@ func __make_move(move: Dictionary):
break
# if big pawn move, update the en passant square
- if move.flags & BITS.BIG_PAWN:
- if turn == "b":
- ep_square = move.to - 16
- else:
- ep_square = move.to + 16
- else:
- ep_square = EMPTY
+ ep_square = (move.to + (-16 if turn == BLACK else 16)) if move.flags & BITS.BIG_PAWN else EMPTY
# reset the 50 move counter if a pawn is moved or a piece is captured
- if move.piece == PAWN:
- half_moves = 0
- elif move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE):
- half_moves = 0
- else:
- half_moves += 1
+ half_moves = 0 if move.piece == PAWN or move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE) else (half_moves + 1)
- if turn == BLACK:
- fullmoves += 1
+ fullmoves += 1 if turn == BLACK else 0
turn = __swap_color(turn)
@@ -785,8 +832,6 @@ func __undo_move() -> Dictionary:
var move: Dictionary = old.move
kings = old.kings
turn = old.turn
- if typeof(old.castling.w) != TYPE_INT || typeof(old.castling.b) != TYPE_INT:
- breakpoint
castling = old.castling
ep_square = old.ep_square
half_moves = old.half_moves
@@ -798,16 +843,10 @@ func __undo_move() -> Dictionary:
board[move.from] = board[move.to]
board[move.from].type = move.piece # to undo any promotions
board[move.to] = null
-
if move.flags & BITS.CAPTURE:
board[move.to] = {type = move.captured, color = them}
elif move.flags & BITS.EP_CAPTURE:
- var index
- if us == BLACK:
- index = move.to - 16
- else:
- index = move.to + 16
- board[index] = {type = PAWN, color = them}
+ board[move.to + (-16 if us == BLACK else 16)] = {type = PAWN, color = them}
if move.flags & (BITS.KSIDE_CASTLE | BITS.QSIDE_CASTLE):
var castling_to
@@ -888,7 +927,7 @@ func __move_from_san(move, sloppy := false) -> Dictionary:
to = matches[3]
promotion = matches[4]
- if from.length() == 1:
+ if from and from.length() == 1:
overly_disambiguated = true
var piece_type := infer_piece_type(clean_move)
@@ -1056,7 +1095,7 @@ func pgn() -> String:
move_string += " %s" % __move_to_san(move, __generate_moves({legal = true}))
__make_move(move)
-
+
if move_string.length():
moves.append(move_string)