online multiplayer chess game (note server currently down)
-rw-r--r--.gitignore2
-rw-r--r--Debug.gd2
-rw-r--r--Globals.gd4
-rw-r--r--Grid.gd26
-rw-r--r--Log.gd8
-rw-r--r--PGN/PGN.gd73
-rw-r--r--SanParse/Move.gd73
-rw-r--r--SanParse/SanParse.gd156
-rw-r--r--SanParse/test.gd109
-rw-r--r--Utils.gd182
-rw-r--r--assets/ui/eye.png3
-rw-r--r--assets/ui/eye.png.import35
-rw-r--r--assets/ui/eye_off.png3
-rw-r--r--assets/ui/eye_off.png.import35
-rw-r--r--assets/ui/eyemap.png3
-rw-r--r--assets/ui/eyemap.png.import35
-rw-r--r--assets/ui/svg/eye.svg3
-rw-r--r--assets/ui/svg/eye_off.svg3
-rw-r--r--networking/Network.gd21
-rw-r--r--networking/PacketHandler.gd2
-rw-r--r--pieces/Piece.gd2
-rw-r--r--project.godot34
-rw-r--r--saveload.gd30
-rw-r--r--test.gd109
-rw-r--r--ui/Log.gd3
-rw-r--r--ui/Settings.tscn49
-rw-r--r--ui/StartMenu.tscn14
-rw-r--r--ui/Status.gd1
-rw-r--r--ui/TimerLabels.gd4
-rw-r--r--ui/account/Account.gd75
-rw-r--r--ui/account/Account.tscn114
-rw-r--r--ui/account/Password.gd1
-rw-r--r--ui/account/Restrict.gd8
-rw-r--r--ui/account/Secret.gd20
-rw-r--r--ui/account/Username.gd1
-rw-r--r--ui/account/usernamepass.gd5
-rw-r--r--ui/account/usernamepass.tscn75
-rw-r--r--ui/barbutton/drawbutton.gd3
-rw-r--r--ui/barbutton/resignbutton.gd3
-rw-r--r--ui/colorpicker/ColorPickerButton.tscn1
-rw-r--r--ui/theme/flatblack.tres4
-rw-r--r--ui/theme/main.tres2
42 files changed, 1181 insertions, 155 deletions
diff --git a/.gitignore b/.gitignore
index d7f78ac..1c5ba4b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@ logs/
*.py
.vscode/
exports/
+# for later
+assets/flags/
diff --git a/Debug.gd b/Debug.gd
index 79578f1..8ea2e04 100644
--- a/Debug.gd
+++ b/Debug.gd
@@ -52,7 +52,7 @@ func _draw() -> void:
return
draw_style_box(style, calculate_size())
var i = len(refs)
- while i >= 0:
+ while i > 0:
i -= 1
var pos := Vector2(offset.x, (i + 1) * vertical)
draw_string(font, pos, get_string(refs[i]))
diff --git a/Globals.gd b/Globals.gd
index ad20b71..58a738e 100644
--- a/Globals.gd
+++ b/Globals.gd
@@ -41,10 +41,6 @@ func pack_vars() -> Dictionary:
}
-func turns() -> int:
- return fullmove
-
-
func reset_halfmove() -> void:
halfmove = 0
__nosethalfmove = true
diff --git a/Grid.gd b/Grid.gd
index c7df829..71b0f47 100644
--- a/Grid.gd
+++ b/Grid.gd
@@ -77,13 +77,10 @@ static func print_matrix_pretty(mat: Array) -> void: # print the matrix
print(topper_header) # print the top border
else:
print(middle_header) # print the middle border
- var row := "┃ %s ┃ " % str(8 - j) # init the string
+ var row := "%s%s %s " % [ender, 8 - j, ender] # init the string
for i in range(8): # for each column
var c: Piece = r[i] # get the column
- if c: # if there is a piece
- row += c.mininame + ender # add the shortname
- else: # if there is no piece
- row += " " + ender
+ row += "%s%s" % [c.mininame, ender] if c else " " + ender # add the piece
print(row) # print the string
print("%s\n%s\n%s" % [middish_heads, letter_header, smaller_heads])
@@ -146,9 +143,7 @@ func init_label(labelscene: PackedScene, i: int, position: Vector2, text: String
func threefoldrepetition() -> int:
- if !history_matrixes.values():
- return 0
- return history_matrixes.values().max()
+ return 0 if !history_matrixes.values() else history_matrixes.values().max()
func mat2str(mat: Array = matrix) -> String:
@@ -156,13 +151,9 @@ func mat2str(mat: Array = matrix) -> String:
for y in range(8):
for x in range(8):
var spot: Piece = mat[y][x]
- if spot:
- string += spot.mininame
- else:
- string += "*"
- for i in mat[8].keys(): # store the metadata
- var thing = mat[8][i]
- string += i + str(thing)
+ string += spot.mininame if spot else "*"
+ for key in mat[8].keys(): # store the metadata
+ string += "%s:%s" % [key, mat[8][key]]
return string
@@ -177,7 +168,7 @@ func drawed(reason := "") -> void:
func win(winner: bool, reason := "") -> void:
ui.set_status("%s won the game by %s" % ["white" if winner else "black", reason], 0) # black won the game by checkmate
Events.emit_signal("game_over")
- Log.info("%s won the game in %s turns!" % ["white" if winner else "black", Globals.turns()])
+ Log.info("%s won the game in %s turns!" % ["white" if winner else "black", Globals.fullmove])
SoundFx.play("Victory")
yield(get_tree().create_timer(5), "timeout")
Events.emit_signal("go_back")
@@ -189,12 +180,11 @@ func check_in_check(prin := false) -> bool: # check if in_check
var spot: Piece = matrix[i][j] # get the square
if spot and spot.white != Globals.turn: # enemie
if spot.can_attack_piece(Globals.white_king if Globals.turn else Globals.black_king): # if it can take the king
- # control never flows here
if prin:
+ # control never flows here
Globals.in_check = true # set in_check
Globals.checking_piece = spot # set checking_piece
SoundFx.play("Check")
- Log.info("check")
return true # stop at the first check found
return false
diff --git a/Log.gd b/Log.gd
index e4a5d26..5899abb 100644
--- a/Log.gd
+++ b/Log.gd
@@ -2,16 +2,16 @@ extends Node
static func info(information) -> void: # logs the input string
- print("[i] " + to_str(information))
+ print("[i] %s" % to_str(information))
static func debug(information) -> void: # logs the input string on debug builds
if Debug.debug:
- print("[d] " + to_str(information))
+ print("[d] %s" % to_str(information))
static func err(information) -> void: # logs the input string to stderr
- printerr("[E]" + to_str(information))
+ printerr("[E] %s" % to_str(information))
static func to_str(arg) -> String:
@@ -23,5 +23,5 @@ static func to_str(arg) -> String:
static func arr2str(arr: Array) -> String:
var string := ""
for i in arr:
- string += str(i) + " "
+ string += "%s " % i
return string
diff --git a/PGN/PGN.gd b/PGN/PGN.gd
new file mode 100644
index 0000000..32ca121
--- /dev/null
+++ b/PGN/PGN.gd
@@ -0,0 +1,73 @@
+extends Node
+class_name PGN
+
+
+func parse(string):
+ # put tags into a dictionary,
+ # and the moves into a array
+
+ var tagex = SanParse.compile('^\\[([A-Za-z0-9_]+)\\s+"([^\\r]*)"\\]\\s*$', false)
+ var tagnameex = SanParse.compile("^[A-Za-z0-9_]+\\Z", false)
+ var movetextex = SanParse.compile(
+ "([NBKRQ]?[a-h]?[1-8]?[\\-x]?[a-h][1-8](?:=?[nbrqkNBRQK])?|[PNBRQK]?@[a-h][1-8]|--|Z0|0000|@@@@|O-O(?:-O)?|0-0(?:-0)?)|(\\{.*)|(;.*)|(\\$[0-9]+)|(\\()|(\\))|(\\*|1-0|0-1|1\\/2-1\\/2)|([\\?!]{1,2})",
+ false
+ )
+
+ # get headers
+ var headers := {}
+ var lines = Array(string.split("\n"))
+ while !lines.empty():
+ var line = lines.pop_front().strip_edges()
+ if !line or line[0] in ["%", ";"]:
+ continue
+
+ if line[0] != "[":
+ break
+
+ var tag_match = tagex.search(line)
+ if tag_match:
+ var cap = tag_match.strings
+ if tagnameex.search(cap[1]):
+ headers[cap[1]] = cap[2]
+ else:
+ # invalid headers
+ push_error("invalid headers")
+ return
+ else:
+ break
+
+ var movetext := PoolStringArray()
+ while !lines.empty():
+ var line = lines.pop_front().strip_edges()
+ if !line:
+ break
+ if line[0] in ["%", ";"]:
+ continue
+ for found in movetextex.search_all(line):
+ movetext.append(found.strings[1])
+
+ return [headers, movetext]
+
+
+func _ready():
+ var parsed = parse(
+ """
+ [Event \"F/S Return Match\"]
+ [Site \"Belgrade, Serbia JUG\"]
+ [Date \"1992.11.04\"]
+ [Round \"29\"]
+ [White \"Fischer, Robert J.\"]
+ [Black \"Spassky, Boris V.\"]
+ [Result \"1/2-1/2\"]
+
+ 1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 {This opening is called the Ruy Lopez.}
+ 4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7
+ 11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5
+ Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6
+ 23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5
+ hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5
+ 35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6
+ Nf2 42. g4 Bd3 43. Re6 1/2-1/2
+ """
+ )
+ print(parsed)
diff --git a/SanParse/Move.gd b/SanParse/Move.gd
new file mode 100644
index 0000000..8abfca1
--- /dev/null
+++ b/SanParse/Move.gd
@@ -0,0 +1,73 @@
+class_name Move
+extends Resource
+
+enum CASTLETYPES { NONE, QUEEN_SIDE, KING_SIDE }
+enum CHECKTYPES { NONE, CHECK, CHECKMATE }
+var generated_from := ""
+var piece := 0
+var move_kind: MoveKind
+var promotion := 0
+# var annotation := "" # later
+var check_type := 0
+var is_capture := false
+
+
+func _init(newpiece: int, newmove, capture := false) -> void:
+ piece = newpiece
+ is_capture = capture
+ move_kind = MoveKind.new(newmove)
+
+
+static func castle_type(type: String) -> int:
+ return CASTLETYPES.QUEEN_SIDE if type == "O-O-O" else CASTLETYPES.KING_SIDE
+
+
+func set_check_type(type: String) -> void:
+ match type:
+ "+":
+ check_type = CHECKTYPES.CHECK
+ "#":
+ check_type = CHECKTYPES.CHECKMATE
+ _:
+ check_type = CHECKTYPES.NONE
+
+
+func compile() -> String: # compiles the structure to a san
+ var res := ""
+ match move_kind.type:
+ MoveKind.CASTLE:
+ res += move_kind.to_str()
+ MoveKind.NORMAL:
+ res += Utils.to_str(piece)
+ res += Utils.to_algebraic(move_kind.data[0])
+ res = res + "x" if is_capture else res
+ res += Utils.to_algebraic(move_kind.data[1])
+ res = res + "=" + Utils.to_str(promotion) if promotion != -1 else res
+ match check_type:
+ CHECKTYPES.CHECK:
+ res += "+"
+ CHECKTYPES.CHECKMATE:
+ res += "#"
+ _:
+ pass
+ return res
+
+
+class MoveKind:
+ extends Resource
+ enum { NONE, NORMAL, CASTLE }
+ var type := 0
+ var data # string OR array
+
+ func _init(something):
+ if typeof(something) == TYPE_ARRAY and len(something) == 2:
+ type = NORMAL
+ data = PoolVector2Array(something)
+ elif typeof(something) == TYPE_INT:
+ type = CASTLE
+ data = something
+ else:
+ assert(false)
+
+ func to_str() -> String:
+ return "O-O-O" if data == CASTLETYPES.QUEENSIDE else "O-O"
diff --git a/SanParse/SanParse.gd b/SanParse/SanParse.gd
new file mode 100644
index 0000000..3669672
--- /dev/null
+++ b/SanParse/SanParse.gd
@@ -0,0 +1,156 @@
+extends Node
+class_name SanParser
+
+const end := "(\\+|\\#)?(\\?\\?|\\?|\\?!|!|!!)?$" # annotation
+
+var regexs := {
+ "pawn_move": compile("^([a-h])([1-8])"),
+ "long_pawn_move": compile("^([a-h])([1-8])([a-h])([1-8])"), # long-san
+ "piece_movement": compile("^([KQBNR])([a-h])([1-8])"),
+ "specific_row_piece_movement": compile("^([KQBNR])([0-9])([a-h])([1-8])"),
+ "specific_column_piece_movement": compile("^([KQBNR])([a-h])([a-h])([1-8])"),
+ "long_piece_movement": compile("^([KQBNR])([a-h])([0-9])([a-h])([1-8])"),
+ "pawn_capture": compile("^([a-h])x([a-h])([1-8])(?:=?([KQBNR]))?"),
+ "long_pawn_capture": compile("^([a-h])([1-8])x([a-h])([1-8])(?:=?([KQBNR]))?"),
+ "piece_capture": compile("^([KQBNR])x([a-h])([1-8])"),
+ "specific_column_piece_capture": compile("^([KQBNR])([a-h])x([a-h])([1-8])"),
+ "specific_row_piece_capture": compile("^([KQBNR])([0-9])x([a-h])([1-8])"),
+ "long_piece_capture": compile("^([KQBNR])([a-h])([0-9])x([a-h])([1-8])"),
+ "pawn_promotion": compile("^([a-h])([1-8])=?([KQBNR])"),
+ "castling": compile("^(O-O-O|O-O)"),
+}
+
+
+static func pos(col: String, row: String) -> Vector2:
+ return Utils.from_algebraic(col + row)
+
+
+const UNKNOWN_POS = Vector2(-1, -1)
+enum { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING }
+
+
+func from_str(string: String) -> int:
+ var find = " NBRQK".find(string)
+ if find != -1:
+ return find
+ else:
+ return "PNBRQK".find(string)
+
+
+func compile(regxstr: String, app_end := true) -> RegEx: #app_end because append end get it
+ var reg = RegEx.new()
+ reg.compile(regxstr + end if app_end else regxstr)
+ return reg
+
+
+func parse(san: String) -> Move:
+ var mv = regexmatch(san)
+ mv.generated_from = san # for debugging i just moved the thing over so i can do this
+ return mv
+
+
+func regexmatch(san: String) -> Move:
+ var re: RegExMatch = regexs.pawn_move.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(PAWN, [UNKNOWN_POS, pos(cap[1], cap[2])])
+ mov.set_check_type(cap[3])
+ return mov
+
+ re = regexs.long_pawn_move.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(PAWN, [pos(cap[1], cap[2]), pos(cap[3], cap[4])])
+ mov.set_check_type(cap[5])
+ return mov
+
+ re = regexs.piece_movement.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [UNKNOWN_POS, pos(cap[2], cap[3])])
+ mov.set_check_type(cap[4])
+ return mov
+
+ re = regexs.specific_row_piece_movement.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [Vector2(-1, Utils.row_pos(cap[2])), pos(cap[3], cap[4])])
+ mov.set_check_type(cap[5])
+ return mov
+
+ re = regexs.specific_column_piece_movement.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [Vector2(Utils.col_pos(cap[2]), -1), pos(cap[3], cap[4])])
+ mov.set_check_type(cap[5])
+ return mov
+
+ re = regexs.long_piece_movement.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [pos(cap[2], cap[3]), pos(cap[4], cap[5])])
+ mov.set_check_type(cap[6])
+ return mov
+
+ re = regexs.pawn_capture.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(PAWN, [Vector2(Utils.col_pos(cap[1]), -1), pos(cap[2], cap[3])], true)
+ mov.promotion = from_str(cap[4])
+ mov.set_check_type(cap[5])
+ return mov
+
+ re = regexs.long_pawn_capture.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(PAWN, [pos(cap[1], cap[2]), pos(cap[3], cap[4])], true)
+ mov.promotion = from_str(cap[5])
+ mov.set_check_type(cap[6])
+ return mov
+
+ re = regexs.piece_capture.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [Vector2(Utils.col_pos(cap[2]), -1), pos(cap[3], cap[4])], true)
+ mov.set_check_type(cap[5])
+ return mov
+
+ re = regexs.specific_column_piece_capture.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [Vector2(Utils.col_pos(cap[2]), -1), pos(cap[3], cap[4])], true)
+ mov.set_check_type(cap[5])
+ return mov
+
+ re = regexs.specific_row_piece_capture.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [Vector2(-1, Utils.row_pos(cap[2])), pos(cap[3], cap[4])], true)
+ mov.set_check_type(cap[5])
+ return mov
+
+ re = regexs.long_piece_capture.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(from_str(cap[1]), [pos(cap[2], cap[3]), pos(cap[4], cap[5])], true)
+ mov.set_check_type(cap[6])
+ return mov
+
+ re = regexs.pawn_promotion.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(PAWN, [UNKNOWN_POS, pos(cap[1], cap[2])], true)
+ mov.promotion = from_str(cap[3])
+ mov.set_check_type(cap[4])
+ return mov
+
+ re = regexs.castling.search(san)
+ if re:
+ var cap = re.strings
+ var mov = Move.new(KING, Move.castle_type(cap[1]))
+ mov.set_check_type(cap[2])
+ return mov
+
+ push_error("regex exhausted: no matches(%s)" % san)
+
+ return null
diff --git a/SanParse/test.gd b/SanParse/test.gd
new file mode 100644
index 0000000..4c5bd5d
--- /dev/null
+++ b/SanParse/test.gd
@@ -0,0 +1,109 @@
+extends Node
+
+
+class TestSan:
+ extends SanParser
+
+ func assert_castle(string: String, s):
+ var m = parse(string)
+ assert(m.move_kind.type == Move.MoveKind.CASTLE)
+ assert(m.move_kind.data == s)
+ assert(m.piece == KING)
+ assert(m.promotion == 0)
+ assert(m.check_type == Move.CHECKTYPES.NONE)
+ assert(m.is_capture == false)
+
+ func assert_move(mv: String, start: Vector2, dest: Vector2, piece: int) -> void:
+ assert_all(parse(mv), PoolVector2Array([start, dest]), piece, false)
+
+ func assert_capture(mv: String, start: Vector2, dest: Vector2, piece: int) -> void:
+ assert_all(parse(mv), PoolVector2Array([start, dest]), piece, true)
+
+ func assert_all(mv: Move, vectors: PoolVector2Array, piece: int, capture: bool, promote = -1) -> void:
+ assert(mv.move_kind.type == Move.MoveKind.NORMAL)
+ assert([mv.move_kind.data == vectors, mv.piece == piece, mv.is_capture == capture].min())
+ if promote != -1:
+ assert(mv.promotion == promote)
+
+ func test_algebraic_conversion():
+ assert(Utils.from_algebraic(Utils.to_algebraic(Vector2.ZERO)) == Vector2.ZERO)
+ assert(Utils.from_algebraic(Utils.to_algebraic(Vector2.ONE)) == Vector2.ONE)
+ assert(pos("a", "9") == Utils.from_algebraic("a9"))
+ assert(Utils.col_pos("h") == 7)
+ assert(Utils.row_pos("1") == 7)
+
+ func test_castle_short():
+ assert_castle("O-O", Move.CASTLETYPES.KING_SIDE)
+
+ func test_castle_long():
+ assert_castle("O-O-O", Move.CASTLETYPES.QUEEN_SIDE)
+
+ func test_pawn():
+ assert_move("e4", UNKNOWN_POS, Vector2(4, 4), PAWN)
+
+ func test_pawn_long():
+ assert_move("e2e4", Vector2(4, 6), Vector2(4, 4), PAWN)
+
+ func test_piece():
+ assert_move("Qe4", UNKNOWN_POS, Vector2(4, 4), QUEEN)
+
+ func test_piece_file():
+ assert_move("Qbe4", Vector2(1, -1), Vector2(4, 4), QUEEN)
+
+ func test_piece_rank():
+ assert_move("Q1e4", Vector2(-1, 7), Vector2(4, 4), QUEEN)
+
+ func test_piece_long():
+ assert_move("Qb1e4", Vector2(1, 7), Vector2(4, 4), QUEEN)
+
+ func test_pawn_capture():
+ assert_capture("exd4", Vector2(4, -1), Vector2(3, 4), PAWN)
+
+ func test_pawn_capture_promotion():
+ assert_all(parse("exd8=Q"), PoolVector2Array([Vector2(4, -1), Vector2(3, 0)]), PAWN, true, QUEEN)
+
+ func test_pawn_capture_long():
+ assert_capture("e3xd4", Vector2(4, 5), Vector2(3, 4), PAWN)
+
+ func test_piece_capture():
+ assert_capture("R1xh3", Vector2(-1, 7), Vector2(7, 5), ROOK)
+
+ func test_piece_capture_file():
+ assert_capture("Rexh3", Vector2(4, -1), Vector2(7, 5), ROOK)
+
+ func test_piece_capture_long():
+ assert_capture("Re3xh3", Vector2(4, 5), Vector2(7, 5), ROOK)
+
+ func test_pawn_promotion():
+ assert_all(parse("d8=Q"), PoolVector2Array([UNKNOWN_POS, Vector2(3, 0)]), PAWN, QUEEN)
+
+ func test_compile():
+ var s = Move.new(PAWN, [Vector2(4, -1), Vector2(3, 0)])
+ s.promotion = QUEEN
+ s.check_type = Move.CHECKTYPES.CHECK
+ s.is_capture = true
+ var result = s.compile()
+ assert(result == "exd8=Q+")
+
+ func _init():
+ test_algebraic_conversion()
+ test_castle_short()
+ test_castle_long()
+ test_pawn()
+ test_pawn_long()
+ test_piece()
+ test_piece_file()
+ test_piece_rank()
+ test_piece_long()
+ test_pawn_capture()
+ test_pawn_capture_promotion()
+ test_pawn_capture_long()
+ test_piece_capture()
+ test_piece_capture_file()
+ test_piece_capture_long()
+ test_pawn_promotion()
+ test_compile()
+
+
+func _ready():
+ TestSan.new()
diff --git a/Utils.gd b/Utils.gd
index 3810ff4..c3d570e 100644
--- a/Utils.gd
+++ b/Utils.gd
@@ -1,15 +1,74 @@
extends Node
+var internet := false
signal newmove(move)
signal newfen(fen)
-var turn_moves: PoolStringArray = []
-var turns_moves: PoolStringArray = []
-var internet := false
-var counter := 0
+var moves_list: PoolStringArray = []
var fen := ""
+func get_pgn():
+ return moves_list.join(" ")
+
+
+func _on_turn_over() -> void:
+ fen = get_fen()
+ Log.info("fen: " + fen)
+ emit_signal("newfen", fen)
+
+
+func get_fen() -> String:
+ var pieces := ""
+ for rank in range(8):
+ var empty := 0
+ for file in range(8):
+ var spot: Piece = Globals.grid.matrix[rank][file]
+ if spot == null:
+ empty += 1
+ if len(pieces) > 0 and str(empty - 1) == pieces[-1]:
+ pieces[-1] = str(empty)
+ else:
+ pieces += str(empty)
+ else:
+ pieces += (spot.shortname[0].to_upper() if spot.white else spot.shortname[0].to_lower())
+ empty = 0
+ if rank != 7:
+ pieces += "/"
+ # handle castling checks
+ var whitecastling := PoolStringArray(Globals.white_king.castleing(true)).join("")
+ var blackcastling := PoolStringArray(Globals.black_king.castleing(true)).join("")
+ var castlingrights := ""
+ if blackcastling and whitecastling:
+ castlingrights = whitecastling.to_upper() + blackcastling.to_lower()
+ else:
+ castlingrights = "-"
+
+ var enpassants := ""
+ for pawn in Globals.pawns:
+ if pawn.twostepfirstmove and pawn.just_set:
+ enpassants += Utils.to_algebraic(pawn.real_position + (Vector2.DOWN * pawn.whiteint))
+ return (
+ "%s %s %s %s %s %s"
+ % [
+ pieces,
+ "w" if Globals.turn else "b",
+ castlingrights,
+ enpassants if enpassants else "-",
+ Globals.halfmove,
+ Globals.fullmove,
+ ]
+ ) # pos # turn # castling # enpassant # halfmove # fullmove
+
+
+func add_move(move: String) -> void:
+ if Globals.turn == false:
+ moves_list.append("%s. %s" % [Globals.fullmove, move])
+ else:
+ moves_list.append(move)
+ emit_signal("newmove", move)
+
+
func get_args() -> Dictionary:
var arguments := {}
for argument in OS.get_cmdline_args():
@@ -30,6 +89,7 @@ func _ready() -> void:
print("run with command help to show this help")
get_tree().quit() # dont wait
Debug.monitor(self, "fen")
+ Debug.monitor(self, "pgn", "get_pgn()")
static func exec_ext() -> String:
@@ -52,26 +112,8 @@ static func is_king(inode) -> bool:
return inode is King
-func add_move(move: String) -> void:
- if turn_moves.size() == 0:
- turn_moves.append("%s. %s" % [Globals.fullmove, move])
- else:
- turn_moves.append(move)
- emit_signal("newmove", move)
-
-
func reset_vars() -> void:
- turn_moves.resize(0)
- turns_moves.resize(0)
- counter = 0
-
-
-static func to_algebraic(pos: Vector2) -> String:
- return "abcdefgh"[pos.x] + str(round(8 - pos.y))
-
-
-static func from_algebraic(algebraic_position: String) -> Vector2:
- return Vector2(ord(algebraic_position[0]) - ord("a"), 8 - int(algebraic_position[1]))
+ moves_list.resize(0)
static func get_node_name(node: Node) -> Array:
@@ -101,79 +143,31 @@ func internet_available() -> bool:
return returnable
-func walk_dir(path := "res://assets/pieces") -> PoolStringArray: # walk the directory, finding the asset packs
- var folders: PoolStringArray = [] # init the folders
+func walk_dir(path := "res://assets/pieces", only_dir := true, of_ext := "png", exclude := []) -> PoolStringArray: # walk the directory, finding the asset packs
+ var files := [] # init the files
var dir := Directory.new() # init the directory
if dir.open(path) == OK: # open the directory
dir.list_dir_begin(true) # list the directory
var file_name := dir.get_next() # get the next file
while file_name != "": # while there is a file
- if dir.current_is_dir(): # if the current is a directory
- folders.append(file_name) # add the folder
+ if only_dir:
+ if dir.current_is_dir(): # if the current is a directory
+ files.append(file_name) # add the folder
+ else:
+ var split = file_name.split(".")
+ if split[-1] == of_ext and !split[0] in exclude:
+ files.append(split[0]) # add the file
file_name = dir.get_next() # get the next file
else:
- Log.err("An error occurred when trying to access the path " + path) # print the error
- return folders # return the folders
+ push_error("An error occurred when trying to access the path " + path) # print the error
+ files.sort() # sort the files
+ return PoolStringArray(files) # return the files
func format_seconds(time: float, use_milliseconds: bool = false) -> String:
return "%02d:%04.1f" if use_milliseconds else "%02d:%02d" % [time / 60, fmod(time, 60)]
-func _on_turn_over() -> void:
- fen = get_fen()
- Log.info("fen: " + fen)
- emit_signal("newfen", fen)
- counter += 1
- if counter >= 2:
- counter = 0
- turns_moves.append(turn_moves.join(" "))
- turn_moves.resize(0)
-
-
-func get_fen() -> String:
- var pieces := ""
- for rank in range(8):
- var empty := 0
- for file in range(8):
- var spot: Piece = Globals.grid.matrix[rank][file]
- if spot == null:
- empty += 1
- if len(pieces) > 0 and str(empty - 1) == pieces[-1]:
- pieces[-1] = str(empty)
- else:
- pieces += str(empty)
- else:
- pieces += (spot.shortname[0].to_upper() if spot.white else spot.shortname[0].to_lower())
- empty = 0
- if rank != 7:
- pieces += "/"
- # handle castling checks
- var whitecastling := PoolStringArray(Globals.white_king.castleing(true)).join("")
- var blackcastling := PoolStringArray(Globals.black_king.castleing(true)).join("")
- var castlingrights := ""
- if blackcastling and whitecastling:
- castlingrights = whitecastling.to_upper() + blackcastling.to_lower()
- else:
- castlingrights = "-"
-
- var enpassants := ""
- for pawn in Globals.pawns:
- if pawn.twostepfirstmove and pawn.just_set:
- enpassants += to_algebraic(pawn.real_position + (Vector2.DOWN * pawn.whiteint))
- return (
- "%s %s %s %s %s %s"
- % [
- pieces,
- "w" if Globals.turn else "b",
- castlingrights,
- enpassants if enpassants else "-",
- Globals.halfmove,
- Globals.fullmove,
- ]
- ) # pos # turn # castling # enpassant # halfmove # fullmove
-
-
func _notification(what: int) -> void:
if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST or what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST:
if get_tree().get_root().has_node("Board"):
@@ -181,3 +175,25 @@ func _notification(what: int) -> void:
yield(get_tree(), "idle_frame") # wait for the packet to send
Log.debug("Bye!")
get_tree().quit()
+
+
+static func to_algebraic(pos: Vector2) -> String:
+ var column = "abcdefgh"[pos.x] if pos.x != -1 else ""
+ var row = str(round(8 - pos.y)) if pos.y != -1 else ""
+ return column + row
+
+
+static func col_pos(col: String) -> int:
+ return "abcdefgh".find(col)
+
+
+static func row_pos(row: String) -> int:
+ return 8 - int(row)
+
+
+static func from_algebraic(pos: String) -> Vector2:
+ return Vector2(col_pos(pos[0]), row_pos(pos[1]))
+
+
+static func to_str(type: int) -> String:
+ return " NBRQK"[type].strip_edges() # if its a pawn, return nothing
diff --git a/assets/ui/eye.png b/assets/ui/eye.png
new file mode 100644
index 0000000..52c248f
--- /dev/null
+++ b/assets/ui/eye.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:471e59bdc371b6634874605113a686beaac9088d8b1fc31f7882046f34a53338
+size 9722
diff --git a/assets/ui/eye.png.import b/assets/ui/eye.png.import
new file mode 100644
index 0000000..c8a951c
--- /dev/null
+++ b/assets/ui/eye.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/eye.png-0766d2e31698a687d5f47fc9c0ab5dce.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/ui/eye.png"
+dest_files=[ "res://.import/eye.png-0766d2e31698a687d5f47fc9c0ab5dce.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/assets/ui/eye_off.png b/assets/ui/eye_off.png
new file mode 100644
index 0000000..26d8738
--- /dev/null
+++ b/assets/ui/eye_off.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5fc4a9b68b92d974c8f8b4a67f7f668d35bd6930c2d1e385f294b281f738d556
+size 10816
diff --git a/assets/ui/eye_off.png.import b/assets/ui/eye_off.png.import
new file mode 100644
index 0000000..315311f
--- /dev/null
+++ b/assets/ui/eye_off.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/eye_off.png-c5b39170125b2d6fd97f423ccac6cd2a.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/ui/eye_off.png"
+dest_files=[ "res://.import/eye_off.png-c5b39170125b2d6fd97f423ccac6cd2a.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/assets/ui/eyemap.png b/assets/ui/eyemap.png
new file mode 100644
index 0000000..903165e
--- /dev/null
+++ b/assets/ui/eyemap.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0f1a48f12ac71c5d72663bd1a05b6588806cafbc7bf8ed694385ee02ca716213
+size 552
diff --git a/assets/ui/eyemap.png.import b/assets/ui/eyemap.png.import
new file mode 100644
index 0000000..ad4e506
--- /dev/null
+++ b/assets/ui/eyemap.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/eyemap.png-fa46d6ece12070f161a31275cd239843.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/ui/eyemap.png"
+dest_files=[ "res://.import/eyemap.png-fa46d6ece12070f161a31275cd239843.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=false
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/assets/ui/svg/eye.svg b/assets/ui/svg/eye.svg
new file mode 100644
index 0000000..3dc7e34
--- /dev/null
+++ b/assets/ui/svg/eye.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1608612fa07dbb57549659adf50718d30f6355f672ebeab9153239a3ddeffe71
+size 1676
diff --git a/assets/ui/svg/eye_off.svg b/assets/ui/svg/eye_off.svg
new file mode 100644
index 0000000..9999dc8
--- /dev/null
+++ b/assets/ui/svg/eye_off.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:daf53aa92e7d3843452bc3350953f3e3a9b5c830e5ed730ccd367229d6a9874b
+size 1906
diff --git a/networking/Network.gd b/networking/Network.gd
index 7ac4590..4633ef6 100644
--- a/networking/Network.gd
+++ b/networking/Network.gd
@@ -11,6 +11,8 @@ const HEADERS := {
"hostrequest": "H",
"stopgame": "K",
"ping": "P",
+ "signup": "C",
+ "signin": ">",
"relay": "R", # relay goes to both
"signal": "S", # signal is one way
}
@@ -36,6 +38,10 @@ signal game_over(problem, isok)
signal connection_established
signal signal_recieved(what)
+## for accounts
+signal signinresult(what)
+signal signupresult(what)
+
const url := "wss://gd-chess-server.herokuapp.com/"
@@ -53,6 +59,14 @@ func _ready() -> void:
t.connect("timeout", self, "ping")
+func signin(data):
+ send_packet(data, HEADERS.signin)
+
+
+func signup(data):
+ send_packet(data, HEADERS.signup)
+
+
func ping() -> void:
send_packet("ping", HEADERS.ping)
@@ -99,8 +113,6 @@ func _data_recieved() -> void:
var recieve: Dictionary = ws.get_peer(1).get_var()
var header: String = recieve.header
var text = recieve.data
- if header != HEADERS.ping:
- Log.debug("recieved %s of header %s" % [text, header])
match header:
HEADERS.hostrequest:
emit_signal("host_result", text)
@@ -111,7 +123,6 @@ func _data_recieved() -> void:
else:
match relay.type:
RELAYHEADERS.startgame:
- print("Start")
emit_signal("start_game")
HEADERS.joinrequest:
emit_signal("join_result", text)
@@ -124,6 +135,10 @@ func _data_recieved() -> void:
emit_signal("signal_recieved", signal)
HEADERS.ping:
pass
+ HEADERS.signup:
+ emit_signal("signupresult", text)
+ HEADERS.signin:
+ emit_signal("signinresult", text)
_:
Log.err("unknown header %s" % header)
diff --git a/networking/PacketHandler.gd b/networking/PacketHandler.gd
index 5034a4b..9cb6bbd 100644
--- a/networking/PacketHandler.gd
+++ b/networking/PacketHandler.gd
@@ -26,7 +26,7 @@ func return() -> void: # return to the void
func _ready() -> void:
get_tree().set_auto_accept_quit(false)
- if Utils.internet:
+ if Utils.internet and get_tree().get_root().has_node("StartMenu"):
var net := Network.new()
Events.connect("go_back", self, "_handle_game_over")
net.connect("move_data", self, "_on_data")
diff --git a/pieces/Piece.gd b/pieces/Piece.gd
index 6d9b09f..299d3a9 100644
--- a/pieces/Piece.gd
+++ b/pieces/Piece.gd
@@ -161,7 +161,7 @@ func get_attacks(check_spots_check := true) -> PoolVector2Array: # @Override
return final
-func can_attack_piece(piece: Piece) -> bool:
+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
diff --git a/project.godot b/project.godot
index dcd2743..6b4aec9 100644
--- a/project.godot
+++ b/project.godot
@@ -84,6 +84,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://ui/Log.gd"
}, {
+"base": "Resource",
+"class": "Move",
+"language": "GDScript",
+"path": "res://SanParse/Move.gd"
+}, {
"base": "Node",
"class": "NetManager",
"language": "GDScript",
@@ -99,6 +104,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://ui/colorpicker/OldColorView.gd"
}, {
+"base": "Node",
+"class": "PGN",
+"language": "GDScript",
+"path": "res://PGN/PGN.gd"
+}, {
"base": "Piece",
"class": "Pawn",
"language": "GDScript",
@@ -124,12 +134,22 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://ui/barbutton/resignbutton.gd"
}, {
+"base": "LineEdit",
+"class": "Restrict",
+"language": "GDScript",
+"path": "res://ui/account/Restrict.gd"
+}, {
"base": "Piece",
"class": "Rook",
"language": "GDScript",
"path": "res://pieces/Rook.gd"
}, {
"base": "Node",
+"class": "SanParser",
+"language": "GDScript",
+"path": "res://SanParse/SanParse.gd"
+}, {
+"base": "Node",
"class": "SaveLoader",
"language": "GDScript",
"path": "res://saveload.gd"
@@ -138,6 +158,11 @@ _global_script_classes=[ {
"class": "StatusLabel",
"language": "GDScript",
"path": "res://ui/Status.gd"
+}, {
+"base": "Control",
+"class": "UsernamePass",
+"language": "GDScript",
+"path": "res://ui/account/usernamepass.gd"
} ]
_global_script_class_icons={
"BarTextureButton": "",
@@ -155,17 +180,22 @@ _global_script_class_icons={
"Knight": "res://assets/pieces/california/wN.png",
"Lobby": "",
"Log": "",
+"Move": "",
"NetManager": "",
"Network": "",
"OldColorView": "",
+"PGN": "",
"Pawn": "res://assets/pieces/california/wP.png",
"Piece": "res://assets/pieces/california/wP.png",
"Preview": "",
"Queen": "res://assets/pieces/california/wQ.png",
"ResignButton": "res://assets/ui/flag.png",
+"Restrict": "",
"Rook": "res://assets/pieces/california/wR.png",
+"SanParser": "",
"SaveLoader": "",
-"StatusLabel": ""
+"StatusLabel": "",
+"UsernamePass": ""
}
[application]
@@ -190,6 +220,8 @@ SaveLoad="*res://saveload.gd"
ColorBack="*res://ui/background/ColorfullBackground.tscn"
PacketHandler="*res://networking/PacketHandler.gd"
Debug="*res://Debug.gd"
+SanParse="*res://SanParse/SanParse.gd"
+Pgn="*res://PGN/PGN.gd"
[debug]
diff --git a/saveload.gd b/saveload.gd
index c77f97b..65866e1 100644
--- a/saveload.gd
+++ b/saveload.gd
@@ -2,6 +2,7 @@ extends Node
class_name SaveLoader
const settings_file := "user://chess.settings"
+const id := "user://.chess.id"
const default_settings_data = {
"vsync": OS.vsync_enabled,
@@ -12,28 +13,47 @@ const default_settings_data = {
"board_color2": Color(0.54902, 0.635294, 0.678431)
}
-var files := {"settings": {"file": settings_file, "data": default_settings_data.duplicate(true)}} # file types
+var files := {
+ "settings": {"file": settings_file, "data": default_settings_data.duplicate(true)},
+ "id": {"file": id, "data": {"id": "", "name": "", "country": "rainbow", "password": ""}}
+} # file types
func _ready() -> void:
+ # Debug.monitor(self, "id data", "files.id.data")
SaveLoad.load_data("settings")
+ SaveLoad.load_data("id")
+
+
+func to_base64(variant) -> String:
+ return Marshalls.variant_to_base64(variant)
+
+
+func from_base64(base64):
+ return Marshalls.base64_to_variant(base64)
func save(type: String) -> void:
var file := File.new()
file.open(files[type]["file"], File.WRITE)
- file.store_string(var2str(files[type]["data"]))
+ file.store_string(to_base64(files[type]["data"]))
func load_data(type: String) -> Dictionary:
if check_file(type):
var file := File.new()
file.open(files[type]["file"], File.READ)
- if file.get_as_text().length() > 0:
- var read_dictionary: Dictionary = str2var(file.get_as_text())
- if files[type]["data"].size() == read_dictionary.size():
+ var text = file.get_as_text()
+ if len(text) > 0:
+ var read_dictionary = from_base64(text)
+ if typeof(read_dictionary) != TYPE_DICTIONARY:
+ save(type) # OVERWRITE
+ elif files[type]["data"].size() == read_dictionary.size():
files[type]["data"] = read_dictionary
+ save(type) # overwrite.
file.close()
+ else:
+ save(type)
return files[type]["data"]
diff --git a/test.gd b/test.gd
new file mode 100644
index 0000000..4c5bd5d
--- /dev/null
+++ b/test.gd
@@ -0,0 +1,109 @@
+extends Node
+
+
+class TestSan:
+ extends SanParser
+
+ func assert_castle(string: String, s):
+ var m = parse(string)
+ assert(m.move_kind.type == Move.MoveKind.CASTLE)
+ assert(m.move_kind.data == s)
+ assert(m.piece == KING)
+ assert(m.promotion == 0)
+ assert(m.check_type == Move.CHECKTYPES.NONE)
+ assert(m.is_capture == false)
+
+ func assert_move(mv: String, start: Vector2, dest: Vector2, piece: int) -> void:
+ assert_all(parse(mv), PoolVector2Array([start, dest]), piece, false)
+
+ func assert_capture(mv: String, start: Vector2, dest: Vector2, piece: int) -> void:
+ assert_all(parse(mv), PoolVector2Array([start, dest]), piece, true)
+
+ func assert_all(mv: Move, vectors: PoolVector2Array, piece: int, capture: bool, promote = -1) -> void:
+ assert(mv.move_kind.type == Move.MoveKind.NORMAL)
+ assert([mv.move_kind.data == vectors, mv.piece == piece, mv.is_capture == capture].min())
+ if promote != -1:
+ assert(mv.promotion == promote)
+
+ func test_algebraic_conversion():
+ assert(Utils.from_algebraic(Utils.to_algebraic(Vector2.ZERO)) == Vector2.ZERO)
+ assert(Utils.from_algebraic(Utils.to_algebraic(Vector2.ONE)) == Vector2.ONE)
+ assert(pos("a", "9") == Utils.from_algebraic("a9"))
+ assert(Utils.col_pos("h") == 7)
+ assert(Utils.row_pos("1") == 7)
+
+ func test_castle_short():
+ assert_castle("O-O", Move.CASTLETYPES.KING_SIDE)
+
+ func test_castle_long():
+ assert_castle("O-O-O", Move.CASTLETYPES.QUEEN_SIDE)
+
+ func test_pawn():
+ assert_move("e4", UNKNOWN_POS, Vector2(4, 4), PAWN)
+
+ func test_pawn_long():
+ assert_move("e2e4", Vector2(4, 6), Vector2(4, 4), PAWN)
+
+ func test_piece():
+ assert_move("Qe4", UNKNOWN_POS, Vector2(4, 4), QUEEN)
+
+ func test_piece_file():
+ assert_move("Qbe4", Vector2(1, -1), Vector2(4, 4), QUEEN)
+
+ func test_piece_rank():
+ assert_move("Q1e4", Vector2(-1, 7), Vector2(4, 4), QUEEN)
+
+ func test_piece_long():
+ assert_move("Qb1e4", Vector2(1, 7), Vector2(4, 4), QUEEN)
+
+ func test_pawn_capture():
+ assert_capture("exd4", Vector2(4, -1), Vector2(3, 4), PAWN)
+
+ func test_pawn_capture_promotion():
+ assert_all(parse("exd8=Q"), PoolVector2Array([Vector2(4, -1), Vector2(3, 0)]), PAWN, true, QUEEN)
+
+ func test_pawn_capture_long():
+ assert_capture("e3xd4", Vector2(4, 5), Vector2(3, 4), PAWN)
+
+ func test_piece_capture():
+ assert_capture("R1xh3", Vector2(-1, 7), Vector2(7, 5), ROOK)
+
+ func test_piece_capture_file():
+ assert_capture("Rexh3", Vector2(4, -1), Vector2(7, 5), ROOK)
+
+ func test_piece_capture_long():
+ assert_capture("Re3xh3", Vector2(4, 5), Vector2(7, 5), ROOK)
+
+ func test_pawn_promotion():
+ assert_all(parse("d8=Q"), PoolVector2Array([UNKNOWN_POS, Vector2(3, 0)]), PAWN, QUEEN)
+
+ func test_compile():
+ var s = Move.new(PAWN, [Vector2(4, -1), Vector2(3, 0)])
+ s.promotion = QUEEN
+ s.check_type = Move.CHECKTYPES.CHECK
+ s.is_capture = true
+ var result = s.compile()
+ assert(result == "exd8=Q+")
+
+ func _init():
+ test_algebraic_conversion()
+ test_castle_short()
+ test_castle_long()
+ test_pawn()
+ test_pawn_long()
+ test_piece()
+ test_piece_file()
+ test_piece_rank()
+ test_piece_long()
+ test_pawn_capture()
+ test_pawn_capture_promotion()
+ test_pawn_capture_long()
+ test_piece_capture()
+ test_piece_capture_file()
+ test_piece_capture_long()
+ test_pawn_promotion()
+ test_compile()
+
+
+func _ready():
+ TestSan.new()
diff --git a/ui/Log.gd b/ui/Log.gd
index d4ac1b4..83e3e07 100644
--- a/ui/Log.gd
+++ b/ui/Log.gd
@@ -21,8 +21,7 @@ static func to_str(arg) -> String:
elif typeof(arg) == TYPE_STRING:
return arg
else:
- err("Called with invalid arguments")
- return ""
+ return str(arg)
static func arr2str(arr: Array) -> String:
diff --git a/ui/Settings.tscn b/ui/Settings.tscn
index f12eddd..3faf8e3 100644
--- a/ui/Settings.tscn
+++ b/ui/Settings.tscn
@@ -21,10 +21,9 @@ size_flags_vertical = 0
alignment = 1
[node name="TabContainer" type="TabContainer" parent="H"]
-margin_left = 203.0
-margin_right = 703.0
+margin_left = 239.0
+margin_right = 668.0
margin_bottom = 570.0
-rect_min_size = Vector2( 500, 0 )
size_flags_vertical = 0
custom_constants/hseparation = 20
drag_to_rearrange_enabled = true
@@ -40,25 +39,25 @@ margin_bottom = -30.0
alignment = 1
[node name="boardcolor1" parent="H/TabContainer/colors" instance=ExtResource( 5 )]
-margin_left = 57.0
+margin_left = 21.0
margin_top = 26.0
-margin_right = 383.0
+margin_right = 347.0
margin_bottom = 132.0
size_flags_horizontal = 4
text = "boardcolor1"
[node name="boardcolor2" parent="H/TabContainer/colors" instance=ExtResource( 5 )]
-margin_left = 57.0
+margin_left = 21.0
margin_top = 147.0
-margin_right = 383.0
+margin_right = 347.0
margin_bottom = 253.0
size_flags_horizontal = 4
text = "boardcolor2"
[node name="PieceSet" type="OptionButton" parent="H/TabContainer/colors"]
-margin_left = 37.0
+margin_left = 1.0
margin_top = 268.0
-margin_right = 403.0
+margin_right = 367.0
margin_bottom = 424.0
focus_mode = 0
size_flags_horizontal = 4
@@ -72,15 +71,17 @@ icon = ExtResource( 3 )
visible = false
anchor_right = 1.0
anchor_bottom = 1.0
-margin_top = 60.0
+margin_left = 30.0
+margin_top = 90.0
+margin_right = -30.0
+margin_bottom = -30.0
mouse_filter = 2
alignment = 1
[node name="VsyncButton" type="CheckBox" parent="H/TabContainer/window"]
-margin_left = 113.0
-margin_top = 65.0
-margin_right = 386.0
-margin_bottom = 205.0
+margin_left = 213.0
+margin_right = 486.0
+margin_bottom = 140.0
focus_mode = 0
size_flags_horizontal = 4
pressed = true
@@ -88,28 +89,28 @@ enabled_focus_mode = 0
text = "vsync"
[node name="FullscreenButton" type="CheckBox" parent="H/TabContainer/window"]
-margin_left = 65.0
-margin_top = 220.0
-margin_right = 434.0
-margin_bottom = 360.0
+margin_left = 165.0
+margin_top = 155.0
+margin_right = 534.0
+margin_bottom = 295.0
focus_mode = 0
size_flags_horizontal = 4
enabled_focus_mode = 0
text = "fullscreen"
[node name="Borderless" type="CheckBox" parent="H/TabContainer/window"]
-margin_left = 91.0
-margin_top = 375.0
-margin_right = 409.0
-margin_bottom = 515.0
+margin_left = 191.0
+margin_top = 310.0
+margin_right = 509.0
+margin_bottom = 450.0
focus_mode = 0
size_flags_horizontal = 4
enabled_focus_mode = 0
text = "borders"
[node name="Container" type="VBoxContainer" parent="H"]
-margin_left = 718.0
-margin_right = 1218.0
+margin_left = 683.0
+margin_right = 1183.0
margin_bottom = 800.0
rect_min_size = Vector2( 300, 0 )
diff --git a/ui/StartMenu.tscn b/ui/StartMenu.tscn
index 1b69847..2d8a117 100644
--- a/ui/StartMenu.tscn
+++ b/ui/StartMenu.tscn
@@ -1,10 +1,12 @@
-[gd_scene load_steps=7 format=2]
+[gd_scene load_steps=9 format=2]
[ext_resource path="res://ui/theme/main.tres" type="Theme" id=1]
[ext_resource path="res://ui/StartMenu.gd" type="Script" id=2]
[ext_resource path="res://ui/Settings.tscn" type="PackedScene" id=3]
[ext_resource path="res://ui/Lobby.tscn" type="PackedScene" id=4]
[ext_resource path="res://assets/ui/verdana-bold.ttf" type="DynamicFontData" id=5]
+[ext_resource path="res://ui/account/Account.tscn" type="PackedScene" id=6]
+[ext_resource path="res://SanParse/test.gd" type="Script" id=7]
[sub_resource type="DynamicFont" id=1]
size = 400
@@ -47,6 +49,13 @@ margin_top = 90.0
margin_right = -30.0
margin_bottom = -30.0
+[node name="account" parent="tabs" instance=ExtResource( 6 )]
+visible = false
+margin_left = 30.0
+margin_top = 90.0
+margin_right = -30.0
+margin_bottom = -30.0
+
[node name="quit" type="Button" parent="tabs"]
visible = false
anchor_right = 1.0
@@ -62,5 +71,8 @@ custom_fonts/font = SubResource( 1 )
enabled_focus_mode = 0
text = "exit"
+[node name="Node2D" type="Node2D" parent="."]
+script = ExtResource( 7 )
+
[connection signal="tab_changed" from="tabs" to="tabs/Lobby" method="_on_tabs_tab_changed"]
[connection signal="pressed" from="tabs/quit" to="." method="_on_quit_pressed"]
diff --git a/ui/Status.gd b/ui/Status.gd
index 351f523..99c6697 100644
--- a/ui/Status.gd
+++ b/ui/Status.gd
@@ -3,7 +3,6 @@ class_name StatusLabel
func set_text(newtext: String, time := 7) -> void:
- print("set to ", newtext)
text = newtext
if time != 0:
yield(get_tree().create_timer(time), "timeout")
diff --git a/ui/TimerLabels.gd b/ui/TimerLabels.gd
index a8d8b7c..59e3374 100644
--- a/ui/TimerLabels.gd
+++ b/ui/TimerLabels.gd
@@ -38,6 +38,6 @@ func _on_game_over() -> void:
func set_color() -> void:
if time > 10:
- colorrect.color = Globals.grid.clockrunning_color if Globals.turn == white else Color.transparent
+ colorrect.color = (Globals.grid.clockrunning_color if Globals.turn == white else Color.transparent)
else:
- colorrect.color = Globals.grid.clockrunninglow if Globals.turn == white else Globals.grid.clocklow
+ colorrect.color = (Globals.grid.clockrunninglow if Globals.turn == white else Globals.grid.clocklow)
diff --git a/ui/account/Account.gd b/ui/account/Account.gd
new file mode 100644
index 0000000..5ee24cf
--- /dev/null
+++ b/ui/account/Account.gd
@@ -0,0 +1,75 @@
+extends Control
+
+onready var flags: PoolStringArray = ["rainbow"]
+onready var flagchoice: OptionButton = $choose/signup/flag
+onready var data: Dictionary = SaveLoad.files.id.data
+onready var status: StatusLabel = $StatusLabel
+onready var signup: UsernamePass = $choose/signup/usernamepass
+onready var signin: UsernamePass = $choose/signin/usernamepass
+
+var autologin = false
+var signed_in = false
+
+
+func _ready():
+ Globals.network.connect("signinresult", self, "_on_signin_result")
+ Globals.network.connect("signupresult", self, "_on_signup_result")
+ Globals.network.connect("connection_established", self, "attempt_autologin")
+ flags.append_array(Utils.walk_dir("res://assets/flags", false, "png", ["rainbow"]))
+ for i in flags: # add the items
+ flagchoice.add_icon_item(load("res://assets/flags/" + i + ".png"), i.replace("_", " "))
+
+
+func attempt_autologin():
+ autologin = true
+ if data.name and data.password:
+ Globals.network.signin(data)
+ Log.info("Attempting autologin")
+ autologin = false
+
+
+func _on_signin_result(result):
+ $choose/signin/signinbutton.disabled = false
+ if typeof(result) == TYPE_STRING: # ew, error, get it away from me
+ Log.err(result)
+ return
+ data.id = result.id
+ data.country = result.country
+ save_data()
+ status.set_text("Sign in sucessfull!")
+ signed_in = true # yay
+
+
+func _on_signup_result(result: String):
+ $choose/signup/signupbutton.disabled = false
+ if "err:" in result: # ew error go awway
+ Log.err(result)
+ return
+ data.id = result
+ save_data()
+ status.set_text("Sign up sucessfull ( you are now logged in )!")
+ signed_in = true # yay
+
+
+func _on_signup_pressed():
+ $choose/signup/signupbutton.disabled = true
+ update_data(signup.username, signup.pw)
+ Globals.network.signup(data)
+
+
+func update_data(username, pw):
+ username.text = username.get_text().strip_edges().strip_escapes()
+ data.name = username.get_text()
+ data.password = pw.get_text()
+ save_data()
+
+
+func save_data():
+ SaveLoad.files.id.data = data
+ SaveLoad.save("id")
+
+
+func _on_signin_pressed():
+ $choose/signin/signinbutton.disabled = true
+ update_data(signin.username, signin.pw)
+ Globals.network.signin(data)
diff --git a/ui/account/Account.tscn b/ui/account/Account.tscn
new file mode 100644
index 0000000..c71e953
--- /dev/null
+++ b/ui/account/Account.tscn
@@ -0,0 +1,114 @@
+[gd_scene load_steps=11 format=2]
+
+[ext_resource path="res://assets/ui/verdana-bold.ttf" type="DynamicFontData" id=1]
+[ext_resource path="res://assets/ui/Roboto-Medium.ttf" type="DynamicFontData" id=2]
+[ext_resource path="res://ui/theme/main.tres" type="Theme" id=3]
+[ext_resource path="res://ui/verdana.tres" type="DynamicFont" id=4]
+[ext_resource path="res://ui/account/usernamepass.tscn" type="PackedScene" id=5]
+[ext_resource path="res://ui/account/Account.gd" type="Script" id=6]
+[ext_resource path="res://ui/Status.gd" type="Script" id=7]
+
+[sub_resource type="DynamicFont" id=1]
+size = 30
+font_data = ExtResource( 2 )
+
+[sub_resource type="DynamicFont" id=3]
+size = 15
+font_data = ExtResource( 1 )
+
+[sub_resource type="Theme" id=2]
+default_font = SubResource( 3 )
+OptionButton/fonts/font = ExtResource( 4 )
+
+[node name="account" type="VBoxContainer"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+theme = ExtResource( 3 )
+script = ExtResource( 6 )
+
+[node name="Disclaimer" type="Label" parent="."]
+margin_right = 1422.0
+margin_bottom = 75.0
+rect_min_size = Vector2( 700, 0 )
+custom_fonts/font = SubResource( 1 )
+text = "Create account, OR play anonymous. When playing anonymously, upon exiting the game, you will be unable to rejoin."
+autowrap = true
+
+[node name="StatusLabel" type="Label" parent="."]
+margin_top = 90.0
+margin_right = 1422.0
+margin_bottom = 140.0
+script = ExtResource( 7 )
+
+[node name="choose" type="TabContainer" parent="."]
+margin_top = 155.0
+margin_right = 1422.0
+margin_bottom = 800.0
+size_flags_vertical = 3
+
+[node name="signup" type="VBoxContainer" parent="choose"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = 30.0
+margin_top = 90.0
+margin_right = -30.0
+margin_bottom = -30.0
+custom_constants/separation = 5
+
+[node name="usernamepass" parent="choose/signup" instance=ExtResource( 5 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_left = 177.0
+margin_right = 1185.0
+margin_bottom = 227.0
+
+[node name="flag" type="OptionButton" parent="choose/signup"]
+visible = false
+margin_left = 531.0
+margin_top = 230.0
+margin_right = 831.0
+margin_bottom = 336.0
+rect_min_size = Vector2( 300, 0 )
+focus_mode = 0
+size_flags_horizontal = 4
+theme = SubResource( 2 )
+text = "flag"
+align = 1
+expand_icon = true
+
+[node name="signupbutton" type="Button" parent="choose/signup"]
+margin_left = 566.0
+margin_top = 232.0
+margin_right = 796.0
+margin_bottom = 338.0
+rect_min_size = Vector2( 230, 0 )
+focus_mode = 0
+size_flags_horizontal = 4
+enabled_focus_mode = 0
+text = "sign up"
+
+[node name="signin" type="VBoxContainer" parent="choose"]
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = 30.0
+margin_top = 90.0
+margin_right = -30.0
+margin_bottom = -30.0
+custom_constants/separation = 5
+
+[node name="usernamepass" parent="choose/signin" instance=ExtResource( 5 )]
+
+[node name="signinbutton" type="Button" parent="choose/signin"]
+margin_left = 577.0
+margin_top = 240.0
+margin_right = 785.0
+margin_bottom = 346.0
+rect_min_size = Vector2( 230, 0 )
+focus_mode = 0
+size_flags_horizontal = 4
+enabled_focus_mode = 0
+text = "sign in"
+
+[connection signal="pressed" from="choose/signup/signupbutton" to="." method="_on_signup_pressed"]
+[connection signal="pressed" from="choose/signin/signinbutton" to="." method="_on_signin_pressed"]
diff --git a/ui/account/Password.gd b/ui/account/Password.gd
new file mode 100644
index 0000000..32db589
--- /dev/null
+++ b/ui/account/Password.gd
@@ -0,0 +1 @@
+extends Restrict
diff --git a/ui/account/Restrict.gd b/ui/account/Restrict.gd
new file mode 100644
index 0000000..5786d80
--- /dev/null
+++ b/ui/account/Restrict.gd
@@ -0,0 +1,8 @@
+extends LineEdit
+class_name Restrict
+
+
+func _on_text_changed(new_text: String):
+ var pos = caret_position
+ text = new_text.strip_edges()
+ caret_position = pos
diff --git a/ui/account/Secret.gd b/ui/account/Secret.gd
new file mode 100644
index 0000000..1410025
--- /dev/null
+++ b/ui/account/Secret.gd
@@ -0,0 +1,20 @@
+extends TextureButton
+
+export(Color) var focusedcolor := Color(0.396078, 0.423529, 0.45098)
+export(Texture) var map_tex
+
+onready var pw := $"../Password"
+
+
+func _ready() -> void:
+ var bitmap := BitMap.new()
+ bitmap.create_from_image_alpha(map_tex.get_data())
+ texture_click_mask = bitmap
+
+
+func _focus(what: bool) -> void:
+ modulate = focusedcolor if what else Color.white
+
+
+func _toggled(button_pressed: bool) -> void:
+ pw.secret = button_pressed
diff --git a/ui/account/Username.gd b/ui/account/Username.gd
new file mode 100644
index 0000000..32db589
--- /dev/null
+++ b/ui/account/Username.gd
@@ -0,0 +1 @@
+extends Restrict
diff --git a/ui/account/usernamepass.gd b/ui/account/usernamepass.gd
new file mode 100644
index 0000000..387b806
--- /dev/null
+++ b/ui/account/usernamepass.gd
@@ -0,0 +1,5 @@
+extends Control
+class_name UsernamePass
+
+onready var username = $Username
+onready var pw = $H/Password
diff --git a/ui/account/usernamepass.tscn b/ui/account/usernamepass.tscn
new file mode 100644
index 0000000..e28da26
--- /dev/null
+++ b/ui/account/usernamepass.tscn
@@ -0,0 +1,75 @@
+[gd_scene load_steps=9 format=2]
+
+[ext_resource path="res://assets/ui/eyemap.png" type="Texture" id=1]
+[ext_resource path="res://ui/account/Secret.gd" type="Script" id=2]
+[ext_resource path="res://assets/ui/eye.png" type="Texture" id=3]
+[ext_resource path="res://assets/ui/eye_off.png" type="Texture" id=4]
+[ext_resource path="res://ui/theme/main.tres" type="Theme" id=5]
+[ext_resource path="res://ui/account/usernamepass.gd" type="Script" id=6]
+[ext_resource path="res://ui/account/Password.gd" type="Script" id=7]
+[ext_resource path="res://ui/account/Username.gd" type="Script" id=8]
+
+[node name="usernamepass" type="VBoxContainer"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+rect_min_size = Vector2( 0, 225 )
+size_flags_horizontal = 4
+size_flags_vertical = 4
+theme = ExtResource( 5 )
+alignment = 1
+script = ExtResource( 6 )
+
+[node name="Username" type="LineEdit" parent="."]
+margin_left = 361.0
+margin_top = 286.0
+margin_right = 1061.0
+margin_bottom = 392.0
+rect_min_size = Vector2( 700, 0 )
+size_flags_horizontal = 4
+size_flags_vertical = 0
+max_length = 20
+placeholder_text = "username goes here"
+caret_blink = true
+caret_blink_speed = 0.5
+script = ExtResource( 8 )
+
+[node name="H" type="HBoxContainer" parent="."]
+margin_top = 407.0
+margin_right = 1422.0
+margin_bottom = 513.0
+custom_constants/separation = 2
+alignment = 1
+
+[node name="Password" type="LineEdit" parent="H"]
+margin_left = 207.0
+margin_right = 1107.0
+margin_bottom = 106.0
+rect_min_size = Vector2( 900, 0 )
+size_flags_horizontal = 4
+size_flags_vertical = 4
+max_length = 20
+context_menu_enabled = false
+placeholder_text = "password goes here"
+caret_blink = true
+caret_blink_speed = 0.5
+script = ExtResource( 7 )
+
+[node name="Secret" type="TextureButton" parent="H"]
+margin_left = 1109.0
+margin_right = 1215.0
+margin_bottom = 106.0
+rect_min_size = Vector2( 106, 106 )
+focus_mode = 1
+toggle_mode = true
+enabled_focus_mode = 1
+texture_normal = ExtResource( 3 )
+texture_pressed = ExtResource( 4 )
+expand = true
+script = ExtResource( 2 )
+focusedcolor = Color( 0.921569, 0.827451, 0.827451, 1 )
+map_tex = ExtResource( 1 )
+
+[connection signal="text_changed" from="Username" to="Username" method="_on_text_changed"]
+[connection signal="text_changed" from="H/Password" to="H/Password" method="_on_text_changed"]
+[connection signal="mouse_entered" from="H/Secret" to="H/Secret" method="_focus" binds= [ true ]]
+[connection signal="mouse_exited" from="H/Secret" to="H/Secret" method="_focus" binds= [ false ]]
diff --git a/ui/barbutton/drawbutton.gd b/ui/barbutton/drawbutton.gd
index 13f4965..6aa3cb4 100644
--- a/ui/barbutton/drawbutton.gd
+++ b/ui/barbutton/drawbutton.gd
@@ -8,7 +8,8 @@ var waiting_on_answer := false
func _ready():
- Globals.network.connect("signal_recieved", self, "_on_signal")
+ if Globals.network:
+ Globals.network.connect("signal_recieved", self, "_on_signal")
func _on_signal(what: Dictionary):
diff --git a/ui/barbutton/resignbutton.gd b/ui/barbutton/resignbutton.gd
index 2a03c3e..e66c42a 100644
--- a/ui/barbutton/resignbutton.gd
+++ b/ui/barbutton/resignbutton.gd
@@ -9,7 +9,8 @@ var waiting_on_answer = false
func _ready() -> void:
- Globals.network.connect("signal_recieved", self, "resigned")
+ if Globals.network:
+ Globals.network.connect("signal_recieved", self, "resigned")
func resigned(what: Dictionary):
diff --git a/ui/colorpicker/ColorPickerButton.tscn b/ui/colorpicker/ColorPickerButton.tscn
index df3ed57..36c89d8 100644
--- a/ui/colorpicker/ColorPickerButton.tscn
+++ b/ui/colorpicker/ColorPickerButton.tscn
@@ -21,7 +21,6 @@ text = "button"
script = ExtResource( 2 )
[node name="Popup" type="PopupPanel" parent="."]
-visible = true
margin_left = 50.0
margin_top = 50.0
margin_right = 195.0
diff --git a/ui/theme/flatblack.tres b/ui/theme/flatblack.tres
index 81baf6d..9aefd64 100644
--- a/ui/theme/flatblack.tres
+++ b/ui/theme/flatblack.tres
@@ -1,5 +1,7 @@
[gd_resource type="StyleBoxFlat" format=2]
[resource]
-bg_color = Color( 0, 0, 0, 1 )
+content_margin_left = 10.0
+content_margin_right = 10.0
+bg_color = Color( 0, 0, 0, 0.882353 )
corner_detail = 20
diff --git a/ui/theme/main.tres b/ui/theme/main.tres
index 333e5d3..c729f76 100644
--- a/ui/theme/main.tres
+++ b/ui/theme/main.tres
@@ -33,6 +33,8 @@ border_width_right = 2
border_width_bottom = 2
border_color = Color( 1, 1, 1, 1 )
corner_detail = 20
+expand_margin_left = 10.0
+expand_margin_right = 10.0
[sub_resource type="StyleBoxFlat" id=10]
content_margin_left = 30.0