Skip to content

Instantly share code, notes, and snippets.

@MaximBazarov
Last active October 6, 2019 11:44
Show Gist options
  • Save MaximBazarov/53bb0ce09b5a30d4ef3cb94c0c51b117 to your computer and use it in GitHub Desktop.
Save MaximBazarov/53bb0ce09b5a30d4ef3cb94c0c51b117 to your computer and use it in GitHub Desktop.
Draft for PGN parsing task
import Foundation
// MARK: - Portable Game Notation (PGN).
/// Portable Game Notation (PGN).
/// https://en.wikipedia.org/wiki/Portable_Game_Notation
struct PGN {
typealias TurnNumber = UInt
let counterparts: [Counterpart]
enum Counterpart {
case turn(TurnNumber, [Counterpart])
case whiteMove(SAN.Move)
case blackMove(SAN.Move)
case moveResult(NAG)
case annotation([Counterpart])
case text(String)
case arrow(from:SAN.Square, to: SAN.Square, color: HighlightColor)
case squareHighlight(SAN.Square, HighlightColor)
case tagPair(Tag, String)
}
/// Tags for tag pairs
enum Tag: String {
case event = "Event"
case site = "Site"
case date = "Date"
case white = "White"
case black = "Black"
case result = "Result"
}
enum HighlightColor: String {
case red = "R"
case green = "G"
case blue = "B"
case yellow = "Y" // R + G
case magenta = "M" // R + B
case cyan = "C" // G + B
}
}
// MARK: - Numeric Annotation Glyphs (NAG)
/// Numeric Annotation Glyphs (NAG)
/// https://en.wikipedia.org/wiki/Numeric_Annotation_Glyphs
// TODO: Implement
public enum NAG: String {
case veryGoodMove = "!!"
case goodMove = "!"
case interestingMove = "!?"
case questionableMove = "?!"
case badMove = "?"
case veryBadMove = "??"
case whiteDecisiveAdvantage = "+\\-" // looks: +\-
case whiteModerateAdvantage = "±"
case whiteSlightAdvantage = "⩲"
case equalPosition = "="
case blackSlightAdvantage = "⩱"
case blackModerateAdvantage = "∓"
case blackDecisiveAdvantage = "-+"
case onlyMove = "□"
case unclearPosition = "∞"
case zugzwang = "⨀"
case developmentAdvantage = "⟳"
case initiative = "↑"
case attack = "→"
case counterplay = "⇆"
case zeitnot = "⊕"
case withIdea = "∆"
case betterIs = "⌓"
case whiteWon = "1-0"
case blackWon = "0-1"
case draft = "1/2-1/2"
case gameOngoing = "*"
}
// MARK: - Standard Algebraic Notation (SAN).
/// Standard Algebraic Notation (SAN).
/// https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
enum SAN {
/// Move e.g. Nxe4
enum Move {
case piece(piece: Piece, from: Square, to: Square)
case castlingQueen(Piece.Color)
case castlingKing(Piece.Color)
init?(_ string: String) {
return nil
}
}
/// Cell in Standard Algebraic Notation (SAN). e.g. e4 or a7
struct Square {
// TODO: Implement
let value: String
init?(_ string: String) {
return nil
}
}
struct Piece {
// TODO: Implement
let value: String
enum Color {
case white
case black
}
init?(_ string: String) {
return nil
}
}
}
// MARK: - Syntax Sugar -
func Turn(_ num: PGN.TurnNumber, _ counterparts: [PGN.Counterpart]) -> PGN.Counterpart {
return PGN.Counterpart.turn(num, counterparts)
}
func WhiteMove(_ string: String) -> PGN.Counterpart? {
guard let san = SAN.Move(string) else { return nil }
return PGN.Counterpart.whiteMove(san)
}
func BlackMove(_ string: String) -> PGN.Counterpart? {
guard let san = SAN.Move(string) else { return nil }
return PGN.Counterpart.blackMove(san)
}
func Annotation(_ counterparts: [PGN.Counterpart]) -> PGN.Counterpart {
return PGN.Counterpart.annotation(counterparts)
}
func Text(_ text: String) -> PGN.Counterpart {
return PGN.Counterpart.text(text)
}
func MoveResult(_ string: String) -> PGN.Counterpart? {
guard let nag = NAG(rawValue: string) else { return nil }
return PGN.Counterpart.moveResult(nag)
}
func ArrowHL(_ from: String, _ to: String, _ color: PGN.HighlightColor) -> PGN.Counterpart {
return PGN.Counterpart.arrow(from: SAN.Square(from)!, to: SAN.Square(to)!, color: color)
}
func SquareHL(_ at: String, _ color: PGN.HighlightColor) -> PGN.Counterpart {
return PGN.Counterpart.squareHighlight(SAN.Square(at)!, color)
}
// MARK: - Examples/Tests -
/*
20.Rd1 ({Not} 20.Rg1 Rxg1+ 21.Kxg1 Re1+ -+)
20...Bg2+ 21.Kg1 Bxf3+ 22.Kf1 Bg2+
(22...Rg2 ! {would have won more quickly. For instance:} 23.Qd3 Rxf2+
24.Kg1 Rg2+ 25.Kh1 Rg1#)
23.Kg1 Bh3+ 24.Kh1 Bxf2 25.Qf1 {Absolutely forced.} 25...Bxf1 26.Rxf1 Re2
27.Ra1 Rh6 28.d4 Be3 0-1
"""
*/
let parsed = PGN(counterparts:[
Turn(20, [
WhiteMove("Rd1")!,
// ---- ({Not} 20.Rg1 Rxg1+ 21.Kxg1 Re1+ -+) -----
Annotation(
[Text("Not"),
Turn(20, [WhiteMove("Rg1")!,
BlackMove("Rxg1+")!,]),
Turn(21, [WhiteMove("Kxg1")!,
BlackMove("Re1+")!,
MoveResult("+-")!,])
]
),
// ------------------------------------------------
BlackMove("Bg2+")!
]
),
// 21.Kg1 Bxf3+
Turn(21,[
WhiteMove("Kg1")!,
BlackMove("Bxf3+")!
]
),
// 22.Kf1 Bg2+
// (22...Rg2 ! {would have won more quickly. For instance:} 23.Qd3 Rxf2+
// 24.Kg1 Rg2+ 25.Kh1 Rg1#)
Turn(22, [
WhiteMove("Kf1")!,
BlackMove("Bg2+")!,
Annotation([
Turn(22, [
BlackMove("Rg2")!,
Text("would have won more quickly. For instance:"),
Turn(23, [
WhiteMove("Qd3")!,
BlackMove("Rxf2+")!
]),
Turn(24, [
WhiteMove("Kg1")!,
BlackMove("Rg2+")!
]),
Turn(25, [
WhiteMove("Kh1")!,
BlackMove("Rg1#")!
])
])
])
]
),
// 23.Kg1 Bh3+ 24.Kh1 Bxf2
Turn(23, [
WhiteMove("Kg1")!,
BlackMove("Bh3+")!
]),
Turn(24, [
WhiteMove("Kh1")!,
BlackMove("Bxf2")!
]),
//25.Qf1 {Absolutely forced.} 25...Bxf1 26.Rxf1 Re2
Turn(25, [
WhiteMove("Qf1")!,
Text("Absolutely forced."),
BlackMove("Bxf1")!
]),
Turn(26, [
WhiteMove("Rxf1")!,
BlackMove("Re2")!
]),
//27.Ra1 Rh6 28.d4 Be3 0-1
Turn(27, [
WhiteMove("Ra1")!,
BlackMove("Rh6")!
]),
Turn(28, [
WhiteMove("d4")!,
BlackMove("Be3 ")!,
MoveResult("0-1")!
]),
]
)
/*
16.Ra2 Rae8 17.Qa6
{[#][%csl Yf3][%cal Rd3f3,Re6g6,Gg6g1] Morphy took twelve minutes
over his next move, probably to assure himself that the combination was sound
and that he had a forced win in every variation.}
17...Qxf3 !! 18.gxf3 Rg6+
*/
let parsed2 = PGN(counterparts: [
Turn(16, [WhiteMove("Ra2")!, BlackMove("Rae8")!]),
Turn(17, [
WhiteMove("Qa6")!,
Annotation([
SquareHL("f3", .yellow),
ArrowHL("d3", "f3", .red),
ArrowHL("e6", "g6", .red),
ArrowHL("g6", "g1", .green),
Text("""
Morphy took twelve minutes
over his next move, probably to assure himself that the combination was sound
and that he had a forced win in every variation.
"""),
BlackMove("Qxf3")!,
MoveResult("!!")!
])
]),
Turn(18, [
WhiteMove("gxf3")!,
BlackMove("Rg6+")!
])
])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment