online multiplayer chess game (note server currently down)
mostly
bendn 2022-06-02
parent 64a86d8 · commit b98c82a
-rw-r--r--Grid.gd30
-rw-r--r--SanParse/Move.gd60
-rw-r--r--SanParse/test.gd2
-rw-r--r--Utils.gd6
-rw-r--r--networking/Network.gd6
-rw-r--r--networking/PacketHandler.gd102
-rw-r--r--pieces/King.gd2
-rw-r--r--pieces/Pawn.gd50
-rw-r--r--pieces/Piece.gd36
-rw-r--r--test.gd1
10 files changed, 177 insertions, 118 deletions
diff --git a/Grid.gd b/Grid.gd
index 71b0f47..6d2b001 100644
--- a/Grid.gd
+++ b/Grid.gd
@@ -319,9 +319,8 @@ func handle_take(position: Vector2) -> void:
if Utils.is_pawn(last_clicked): # if its a pawn
if check_promote(last_clicked, position, "take"):
return
- Globals.network.relay_signal(
- PoolVector2Array([last_clicked.real_position, position]), Network.MOVEHEADERS.move, "positions"
- ) # piece taking piece
+ var mov = Move.new(SanParse.from_str(last_clicked.shortname), [last_clicked.real_position, position], true)
+ Globals.network.send_mov(mov) # piece taking piece
func handle_move(position: Vector2) -> void:
@@ -330,17 +329,8 @@ func handle_move(position: Vector2) -> void:
var castle_data = last_clicked.can_castle[i]
if castle_data[0] == position:
# send some packet
- Globals.network.relay_signal(
- {
- "king": last_clicked.real_position,
- "rook": castle_data[1].real_position,
- "rookdestination": castle_data[2],
- "kingdestination": castle_data[0]
- },
- Network.MOVEHEADERS.castle,
- "positions"
- )
-
+ var mov = Move.new(SanParser.KING, Move.castle_type(castle_data[3]))
+ Globals.network.send_mov(mov)
return
if Utils.is_pawn(last_clicked):
var pawn: Pawn = last_clicked
@@ -349,17 +339,13 @@ func handle_move(position: Vector2) -> void:
var en_passant_data = pawn.enpassant[i]
if en_passant_data[0] == position:
# send some packet
- Globals.network.relay_signal(
- PoolVector2Array([pawn.real_position, position, en_passant_data[1].real_position]),
- Network.MOVEHEADERS.passant,
- "positions"
- )
+ var mov = Move.new(SanParser.PAWN, [pawn.real_position, position])
+ Globals.network.send_mov(mov)
return
elif check_promote(pawn, position):
return
- Globals.network.relay_signal(
- PoolVector2Array([last_clicked.real_position, position]), Network.MOVEHEADERS.move, "positions"
- ) # piece moving
+ var mov = Move.new(SanParse.from_str(last_clicked.shortname), [last_clicked.real_position, position])
+ Globals.network.send_mov(mov)
func check_promote(pawn, position, calltype: String = "move") -> bool:
diff --git a/SanParse/Move.gd b/SanParse/Move.gd
index 8abfca1..82076ea 100644
--- a/SanParse/Move.gd
+++ b/SanParse/Move.gd
@@ -4,9 +4,9 @@ extends Resource
enum CASTLETYPES { NONE, QUEEN_SIDE, KING_SIDE }
enum CHECKTYPES { NONE, CHECK, CHECKMATE }
var generated_from := ""
-var piece := 0
+var piece := -1
var move_kind: MoveKind
-var promotion := 0
+var promotion := -1
# var annotation := "" # later
var check_type := 0
var is_capture := false
@@ -40,19 +40,67 @@ func compile() -> String: # compiles the structure to a san
MoveKind.NORMAL:
res += Utils.to_str(piece)
res += Utils.to_algebraic(move_kind.data[0])
- res = res + "x" if is_capture else res
+ res += "x" if is_capture else ""
res += Utils.to_algebraic(move_kind.data[1])
- res = res + "=" + Utils.to_str(promotion) if promotion != -1 else res
+ res += "=" + Utils.to_str(promotion) if promotion != -1 else ""
match check_type:
CHECKTYPES.CHECK:
res += "+"
CHECKTYPES.CHECKMATE:
res += "#"
- _:
- pass
return res
+## tests
+# print(Utils.to_algebraic(make_long(SanParse.parse("e4").move_kind.data, false, SanParser.PAWN)[0]) == "e2")
+# print(Utils.to_algebraic(make_long(SanParse.parse("Nbc3").move_kind.data, false, SanParser.KNIGHT)[0]) == "b1")
+# print(Utils.to_algebraic(make_long(SanParse.parse("N1c3").move_kind.data, false, SanParser.KNIGHT)[0]) == "b1")
+
+
+# print(Utils.to_algebraic(make_long(SanParse.parse("exe4").move_kind.data, false, SanParser.PAWN)[0]) == "e2")
+# print(Utils.to_algebraic(make_long(SanParse.parse("Nbxc3").move_kind.data, false, SanParser.KNIGHT)[0]) == "b1")
+# print(Utils.to_algebraic(make_long(SanParse.parse("N1xc3").move_kind.data, false, SanParser.KNIGHT)[0]) == "b1")
+### fix short san
+func make_long():
+ var newvecs: PoolVector2Array = []
+
+ var vectors = move_kind.data
+
+ if Piece.is_on_board(vectors[0]): # [0] is the only one with -1(s) possible
+ return vectors
+
+ if is_capture:
+ newvecs.append(long_helper(vectors[0], true, false, vectors[1]))
+ else:
+ newvecs.append(long_helper(vectors[0], false, true, vectors[1]))
+
+ if newvecs.empty():
+ Log.error("cruddlesticks")
+ return
+ newvecs.append(vectors[1])
+
+ move_kind.data = newvecs
+
+
+func long_helper(vec: Vector2, attack: bool, move: bool, touch: Vector2):
+ if vec.y == -1 and vec.x != -1:
+ for y in range(8):
+ var spot = Piece.at_pos(Vector2(vec.x, y))
+ if Utils.spotispiece(piece, spot) and spot.can_touch(touch, attack, move):
+ return Vector2(vec.x, y)
+ elif vec.x == -1 and vec.y != -1:
+ for x in range(8):
+ var spot = Piece.at_pos(Vector2(x, vec.y))
+ if Utils.spotispiece(piece, spot) and spot.can_touch(touch, attack, move):
+ return Vector2(x, vec.y)
+ elif vec == Vector2(-1, -1):
+ for x in range(8):
+ for y in range(8):
+ var spot = Piece.at_pos(Vector2(x, y))
+ if Utils.spotispiece(piece, spot) and spot.can_touch(touch, attack, move):
+ return Vector2(x, y)
+
+
class MoveKind:
extends Resource
enum { NONE, NORMAL, CASTLE }
diff --git a/SanParse/test.gd b/SanParse/test.gd
index 4c5bd5d..137d7b9 100644
--- a/SanParse/test.gd
+++ b/SanParse/test.gd
@@ -9,7 +9,7 @@ class TestSan:
assert(m.move_kind.type == Move.MoveKind.CASTLE)
assert(m.move_kind.data == s)
assert(m.piece == KING)
- assert(m.promotion == 0)
+ assert(m.promotion == -1)
assert(m.check_type == Move.CHECKTYPES.NONE)
assert(m.is_capture == false)
diff --git a/Utils.gd b/Utils.gd
index c3d570e..98e7147 100644
--- a/Utils.gd
+++ b/Utils.gd
@@ -18,6 +18,10 @@ func _on_turn_over() -> void:
emit_signal("newfen", fen)
+func spotispiece(piece_type: int, spot: Piece) -> bool:
+ return SanParse.from_str(spot.shortname.to_upper()) == piece_type if spot else false
+
+
func get_fen() -> String:
var pieces := ""
for rank in range(8):
@@ -118,7 +122,7 @@ func reset_vars() -> void:
static func get_node_name(node: Node) -> Array:
if is_pawn(node):
- return ["♙", "p"] if node.white else ["♟", "p"]
+ return ["♙", "P"] if node.white else ["♟", "P"]
elif node is King:
return ["♔", "K"] if node.white else ["♚", "K"]
elif node is Queen:
diff --git a/networking/Network.gd b/networking/Network.gd
index 4633ef6..10a21e8 100644
--- a/networking/Network.gd
+++ b/networking/Network.gd
@@ -104,6 +104,10 @@ func relay_signal(body, header: String, keyname := "body") -> Dictionary: # its
return signal(body, header, keyname, HEADERS.relay)
+func send_mov(mov: Move):
+ relay_signal(mov.compile(), MOVEHEADERS.move, "move")
+
+
func stopgame(reason: String) -> void:
var packet := {"reason": reason, "gamecode": game_code}
send_packet(packet, HEADERS.stopgame)
@@ -119,7 +123,7 @@ func _data_recieved() -> void:
HEADERS.relay:
var relay: Dictionary = text
if relay.type in MOVEHEADERS.values():
- emit_signal("move_data", text)
+ emit_signal("move_data", text.move)
else:
match relay.type:
RELAYHEADERS.startgame:
diff --git a/networking/PacketHandler.gd b/networking/PacketHandler.gd
index 9cb6bbd..eb96f08 100644
--- a/networking/PacketHandler.gd
+++ b/networking/PacketHandler.gd
@@ -95,44 +95,68 @@ func _start_game() -> void:
lobby.set_buttons(false)
-func _on_data(data: Dictionary) -> void:
+func _on_data(data: String) -> void:
Globals.add_turn()
Log.debug([data, " recieved"])
- Events.emit_signal("data_recieved")
- match data["type"]:
- Network.MOVEHEADERS.passant:
- # en passant
- var end_pos := dict2vec(data["positions"][1])
- var start_piece := Piece.at_pos(dict2vec(data["positions"][0]))
- Piece.at_pos(dict2vec(data["positions"][2])).took() # kill the unfortunate
- start_piece.passant(end_pos)
- Network.MOVEHEADERS.move:
- var start_piece := Piece.at_pos(dict2vec(data["positions"][0]))
- var end_pos := dict2vec(data["positions"][1])
- var end_piece := Piece.at_pos(end_pos)
- if end_piece != null:
- start_piece.take(end_piece)
- else:
- start_piece.moveto(end_pos)
- Network.MOVEHEADERS.castle:
- var king := Piece.at_pos(dict2vec(data["positions"]["king"]))
- var rook := Piece.at_pos(dict2vec(data["positions"]["rook"]))
- rook.moveto(dict2vec(data["positions"]["rookdestination"]), true, false, true)
- Utils.add_move(king.castle(dict2vec(data["positions"]["kingdestination"])))
- Network.MOVEHEADERS.promote:
- var dict_data: Dictionary = data["positions"] # positions is a dict for readability sometimes
- var pawn: Pawn = Piece.at_pos(dict2vec(dict_data["start_position"]))
- var dest := dict2vec(dict_data["destination"])
- if Piece.at_pos(dest) != null:
- Piece.at_pos(dest).took() # move the pawn to the new place, killing if necessary
- pawn.clear_clicked()
- Globals.grid.make_piece(dest, Pawn.piece(dict_data["become"]), dict_data["white"]) # create the promotion
- pawn.took() # kill the pawn
- Utils.add_move(dict_data["notation"]) # add a move
- Globals.grid.print_matrix_pretty(Globals.grid.matrix)
- _:
- Log.err("Wtf")
-
-
-static func dict2vec(dict: Dictionary) -> Vector2:
- return Vector2(dict["x"], dict["y"])
+ Utils.add_move(data)
+ var mov = SanParse.parse(data)
+ match mov.move_kind.type:
+ Move.MoveKind.CASTLE:
+ var side = 7 if Globals.turn else 0
+ var rook: Rook
+ var rook_goto: Vector2
+ var kingpos = Vector2(4, side)
+ var king: King = Piece.at_pos(kingpos)
+ var king_goto: Vector2
+ match mov.move_kind.data:
+ Move.CASTLETYPES.KING_SIDE:
+ rook = Piece.at_pos(Vector2(7, side))
+ rook_goto = Vector2(5, side)
+ king_goto = Vector2(6, side)
+ Move.CASTLETYPES.QUEEN_SIDE:
+ rook = Piece.at_pos(Vector2(0, side))
+ rook_goto = Vector2(3, side)
+ king_goto = Vector2(2, side)
+ _:
+ Log.error("Invalid castle type")
+ return
+ rook.moveto(rook_goto)
+ king.castle(king_goto)
+ Move.MoveKind.NORMAL:
+ # this needs to handle enpassant, taking, and promotion.
+ mov.make_long()
+ var positions = mov.move_kind.data
+ if mov.promotion != -1:
+ var promote_to = SanParse.from_str(mov.promotion)
+ Piece.at_pos(positions[0]).promote_to(promote_to, mov.is_capture, positions[1])
+
+ elif mov.is_capture:
+ Piece.at_pos(positions[0]).take(Piece.at_pos(positions[1]))
+
+ elif not Piece.at_pos(positions[1]) and mov.piece == SanParser.PAWN: # if not positions[1] and piece is pawn: enpassant!
+ var pawn: Pawn = Piece.at_pos(positions[0])
+ pawn.passant(positions[1])
+ else: # a very normal move
+ Piece.at_pos(positions[0]).moveto(positions[1])
+
+ # Events.emit_signal("data_recieved")
+ # match data["type"]:
+ # Network.MOVEHEADERS.passant:
+ # # en passant
+ # var end_pos := dict2vec(data["positions"][1])
+ # var start_piece := Piece.at_pos(dict2vec(data["positions"][0]))
+ # Piece.at_pos(dict2vec(data["positions"][2])).took() # kill the unfortunate
+ # start_piece.passant(end_pos)
+ # Network.MOVEHEADERS.move:
+ # var start_piece := Piece.at_pos(dict2vec(data["positions"][0]))
+ # var end_pos := dict2vec(data["positions"][1])
+ # var end_piece := Piece.at_pos(end_pos)
+ # if end_piece != null:
+ # start_piece.take(end_piece)
+ # else:
+ # start_piece.moveto(end_pos)
+
+ # Network.MOVEHEADERS.promote:
+ # var dict_data: Dictionary = data["positions"] # positions is a dict for readability sometimes
+ # _:
+ # Log.err("Wtf")
diff --git a/pieces/King.gd b/pieces/King.gd
index 7360054..5411676 100644
--- a/pieces/King.gd
+++ b/pieces/King.gd
@@ -85,7 +85,7 @@ func castle(position: Vector2) -> String:
return_string = i[3]
break
can_castle.clear()
- moveto(position, true, false, true)
+ moveto(position, true)
return return_string
diff --git a/pieces/Pawn.gd b/pieces/Pawn.gd
index a4078ec..93efb31 100644
--- a/pieces/Pawn.gd
+++ b/pieces/Pawn.gd
@@ -23,7 +23,9 @@ func _ready() -> void:
for i in range(0, 4): # add 3 sprites
var newsprite: Node2D = load("res://ui/ClickableSprite.tscn").instance()
newsprite.position = (sprite.position + Vector2(0, (i * Globals.grid.piece_size.y) * whiteint))
- newsprite.connect("clicked", self, "handle_sprite_input_event", [newsprite])
+ newsprite.get_node("Sprite").texture = load("%s%s%s.png" % [Globals.grid.ASSETS_PATH, team.to_lower(), promotables[i]])
+ newsprite.name = promotables[i]
+ newsprite.connect("clicked", self, "handle_sprite_input_event", [newsprite.name])
newsprite.z_index = 5 # its not a texturebutton so i can use this
newsprite.hide()
add_child(newsprite)
@@ -36,7 +38,7 @@ func _exit_tree() -> void:
Globals.pawns.remove(find)
-func moveto(position: Vector2, real := true, take := false, override_moveto := false) -> void:
+func moveto(position: Vector2, real := true) -> void:
# check if 2 step
if real:
if !twostepfirstmove and !has_moved:
@@ -46,7 +48,7 @@ func moveto(position: Vector2, real := true, take := false, override_moveto := f
if !white and position.y - real_position.y == 2:
twostepfirstmove = true
just_set = true
- .moveto(position, real, take, override_moveto)
+ .moveto(position, real)
if real:
Globals.reset_halfmove()
@@ -74,6 +76,12 @@ static func can_promote(position: Vector2) -> bool:
func passant(position: Vector2) -> void:
+ var to_take = position + Vector2(-1, -1) * whiteint
+ if !at_pos(to_take):
+ if at_pos(to_take + Vector2(2, 0)):
+ to_take += Vector2(2, 0)
+ if at_pos(position):
+ at_pos(position).took()
enpassant.resize(0)
moveto(position)
@@ -124,34 +132,22 @@ func promote(position: Vector2, type: String) -> void:
promoteposition = position
darken.show()
for i in range(len(promotables)):
- sprites[i].sprite.texture = load(
- "%s%s%s.png" % [Globals.grid.ASSETS_PATH, team.to_lower(), promotables[i]]
- )
- sprites[i].name = promotables[i]
sprites[i].show()
-func handle_sprite_input_event(node: Node2D) -> void:
+func promote_to(promote_to: String, is_capture: bool, position: Vector2):
+ if is_capture and at_pos(position):
+ at_pos(position).took()
+ clear_clicked()
+ Globals.grid.make_piece(position, piece(promote_to), white)
+ took()
+
+
+func handle_sprite_input_event(promote_to: String) -> void:
darken.hide()
- var promote_to := node.name
- var first := (
- algebraic_move_notation(promoteposition)
- if !promotetake
- else algebraic_take_notation(promoteposition, real_position)
- )
- Log.debug(promote_to)
- var notation := "%s=%s" % [first, promote_to]
- Globals.network.relay_signal(
- {
- "start_position": real_position,
- "destination": promoteposition,
- "become": promote_to,
- "notation": notation,
- "white": white
- },
- Network.MOVEHEADERS.promote,
- "positions"
- )
+ var mov = Move.new(SanParser.PAWN, [real_position, promoteposition])
+ mov.promotion = SanParse.from_str(promote_to)
+ Globals.network.send_mov(mov)
static func piece(string: String) -> String:
diff --git a/pieces/Piece.gd b/pieces/Piece.gd
index 299d3a9..0c52252 100644
--- a/pieces/Piece.gd
+++ b/pieces/Piece.gd
@@ -67,27 +67,12 @@ func move(newpos: Vector2) -> void: # dont use directly; use moveto
tween.start()
-func moveto(position: Vector2, real := true, take := false, override_moveto := false) -> void:
+func moveto(pos: Vector2, real := true) -> void:
Globals.grid.matrix[real_position.y][real_position.x] = null
- Globals.grid.matrix[position.y][position.x] = self
+ Globals.grid.matrix[pos.y][pos.x] = self
if real:
- if !override_moveto:
- if !take:
- Utils.add_move(algebraic_move_notation(position))
- else:
- Utils.add_move(algebraic_take_notation(position))
- real_position = position
+ real_position = pos
move(real_position)
- Log.debug(
- (
- "%s moving from %s to %s"
- % [
- shortname + " white" if white else " black",
- Utils.to_algebraic((global_position / Globals.grid.piece_size).snapped(Vector2(1, 1))),
- Utils.to_algebraic(real_position)
- ]
- )
- )
SoundFx.play("Move")
has_moved = true
@@ -161,13 +146,24 @@ func get_attacks(check_spots_check := true) -> PoolVector2Array: # @Override
return final
-func can_attack_piece(piece: Piece) -> bool:##i cant use pos in get_attacks for some bizarre reasons
+func can_attack_piece(piece: Piece) -> bool: ##i cant use pos in get_attacks for some bizarre reasons
for pos in get_attacks(false):
if at_pos(pos) == piece:
return true
return false
+func can_touch(pos: Vector2, attack := true, move := true) -> bool:
+ if attack and move:
+ return pos in get_attacks() or pos in get_moves()
+ elif attack:
+ return pos in get_attacks()
+ elif move:
+ return pos in get_moves()
+ else:
+ return false
+
+
static func create_move_circles(pos: Vector2) -> void:
Globals.grid.background_matrix[pos.x][pos.y].set_circle(true) # make the move circle
@@ -205,7 +201,7 @@ static func is_on_board(vector: Vector2) -> bool: # limit the vector to the boa
func take(piece: Piece) -> void:
clear_clicked()
piece.took()
- moveto(piece.real_position, true, true)
+ moveto(piece.real_position, true)
Globals.reset_halfmove()
diff --git a/test.gd b/test.gd
index 4c5bd5d..87552e8 100644
--- a/test.gd
+++ b/test.gd
@@ -84,6 +84,7 @@ class TestSan:
s.is_capture = true
var result = s.compile()
assert(result == "exd8=Q+")
+ assert(Move.parse("e4").compile() == "e4")
func _init():
test_algebraic_conversion()