online multiplayer chess game (note server currently down)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
extends Node
class_name PGN

var movetextex = Utils.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})"
)
var tagex = Utils.compile('^\\[([A-Za-z0-9_]+)\\s+"([^\\r]*)"\\]\\s*$')
var tagnameex = Utils.compile("^[A-Za-z0-9_]+\\Z")


func parse(pgn: String, tags := true) -> Dictionary:
	# put tags into a dictionary,
	# and the moves into a array
	var lines = Array(pgn.split("\n"))
	var headers := {}
	if tags:
		# get headers
		while !lines.empty():
			var line = lines[0].strip_edges()
			if !line or line[0] in ["%", ";"]:
				lines.pop_front()
				continue

			if line[0] != "[":
				break

			lines.pop_front()
			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
					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):
			if found.strings[1]:
				movetext.append(found.strings[1])
	return {"headers": headers, "moves": movetext}