From 79155255f6b1e0c08bc48661be7e9b906b6868a4 Mon Sep 17 00:00:00 2001 From: Owen Rees Date: Thu, 28 May 2026 12:35:03 +0200 Subject: [PATCH] Transfer code (#1) * copy project root files * import previously created code and rename where needed * fix license and lint * main is now just a JS wrapper * minified main.cjs rather than copying to a newly named file --- .ocamlformat | 2 + CHANGELOG.md | 3 + CONTRIBUTING.md | 13 +++ Makefile | 23 +++++ README.md | 9 ++ bin/dune | 8 +- bin/main.ml | 36 ++++++- dune-project | 8 +- lib/ast.ml | 14 +++ lib/dune | 10 +- lib/latex.ml | 115 +++++++++++++++++++++ lib/latex_helpers.ml | 63 ++++++++++++ lib/lexer.ml | 103 +++++++++++++++++++ lib/nag.ml | 32 ++++++ lib/parser.mly | 35 +++++++ lib/parsing.ml | 42 ++++++++ lib/pgn2tex.ml | 5 + pgn_to_tex.opam | 5 +- test/dune | 7 +- test/helpers/dune | 3 + test/helpers/latex_helper.ml | 12 +++ test/pgn_examples/1.pgn | 15 +++ test/pgn_examples/10.pgn | 13 +++ test/pgn_examples/11.pgn | 18 ++++ test/pgn_examples/2.pgn | 18 ++++ test/pgn_examples/3.pgn | 19 ++++ test/pgn_examples/4.pgn | 121 ++++++++++++++++++++++ test/pgn_examples/5.pgn | 120 ++++++++++++++++++++++ test/pgn_examples/6.pgn | 120 ++++++++++++++++++++++ test/pgn_examples/7.pgn | 112 ++++++++++++++++++++ test/pgn_examples/8.pgn | 109 ++++++++++++++++++++ test/pgn_examples/9.pgn | 108 ++++++++++++++++++++ test/test_latex.ml | 182 +++++++++++++++++++++++++++++++++ test/test_lexer.ml | 191 +++++++++++++++++++++++++++++++++++ test/test_nag.ml | 30 ++++++ test/test_pgn2tex.ml | 123 ++++++++++++++++++++++ test/test_pgn_to_tex.ml | 0 test/test_suite.ml | 13 +++ 38 files changed, 1850 insertions(+), 10 deletions(-) create mode 100644 .ocamlformat create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 lib/ast.ml create mode 100644 lib/latex.ml create mode 100644 lib/latex_helpers.ml create mode 100644 lib/lexer.ml create mode 100644 lib/nag.ml create mode 100644 lib/parser.mly create mode 100644 lib/parsing.ml create mode 100644 lib/pgn2tex.ml create mode 100644 test/helpers/dune create mode 100644 test/helpers/latex_helper.ml create mode 100644 test/pgn_examples/1.pgn create mode 100644 test/pgn_examples/10.pgn create mode 100644 test/pgn_examples/11.pgn create mode 100644 test/pgn_examples/2.pgn create mode 100644 test/pgn_examples/3.pgn create mode 100644 test/pgn_examples/4.pgn create mode 100644 test/pgn_examples/5.pgn create mode 100644 test/pgn_examples/6.pgn create mode 100644 test/pgn_examples/7.pgn create mode 100644 test/pgn_examples/8.pgn create mode 100644 test/pgn_examples/9.pgn create mode 100644 test/test_latex.ml create mode 100644 test/test_lexer.ml create mode 100644 test/test_nag.ml create mode 100644 test/test_pgn2tex.ml delete mode 100644 test/test_pgn_to_tex.ml create mode 100644 test/test_suite.ml diff --git a/.ocamlformat b/.ocamlformat new file mode 100644 index 0000000..b24f4b1 --- /dev/null +++ b/.ocamlformat @@ -0,0 +1,2 @@ +profile = default +version = 0.28.1 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4cef770 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## v0.0.1 + +- Initial release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f8bfd00 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via the repository's issues. + +If you are wanting to tackle one of the pre-existing issues, please comment on that issue so that you can be assigned to it. + +Alternatively, just send a PR and I will check it asap. Please bear in mind that I may not be reactive if I was not expecting a PR. + +## Guidelines + +- We use the [GitHub Flow](https://www.gitkraken.com/learn/git/best-practices/git-branch-strategy#github-flow-considerations) branching strategy. Add your code into a feature branch such as `feature/feature-name`. +- Write good commit messages. 'Fix bug' is not a good commit message. 'Fix bug with comments before moves' is a good commit message. +- Explain the changes in the PR comments diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5fad106 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +default: clean build + +clean: + dune clean + +build: + dune build --profile release + +test: + dune runtest + +install-deps: + opam install dune sedlex menhir js_of_ocaml js_of_ocaml-ppx ounit2 ocaml-lsp-server ocamlformat + +check: + opam lint + dune build @fmt @install @runtest @lint + +# bench: +# ocamlopt -o benchmarks/strings unix.cmxa benchmarks/strings.ml +# benchmarks/strings + +.PHONY: clean test \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9db8160 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +## Dependencies + +- dune +- sedlex +- menhir +- js_of_ocaml-compiler +- ocamlformat +- ocaml-lsp-server +- ounit2 diff --git a/bin/dune b/bin/dune index 927ecae..cdc412a 100644 --- a/bin/dune +++ b/bin/dune @@ -1,4 +1,8 @@ (executable - (public_name pgn_to_tex) (name main) - (libraries pgn_to_tex)) + (modes js) + (libraries pgn_to_tex js_of_ocaml yojson) + (preprocess + (pps js_of_ocaml-ppx)) + (enabled_if + (= %{env:CI=false} "false"))) diff --git a/bin/main.ml b/bin/main.ml index 7bf6048..d56bc90 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1 +1,35 @@ -let () = print_endline "Hello, World!" +open Js_of_ocaml +open Pgn_logic +open Ast + +let parse_diagram_json json_str = + let json = Yojson.Basic.from_string json_str in + let json_list = Yojson.Basic.Util.to_list json in + + List.fold_left + (fun acc item -> + let move_int = + item |> Yojson.Basic.Util.member "ply" |> Yojson.Basic.Util.to_int + in + let fen_str = + item |> Yojson.Basic.Util.member "fen" |> Yojson.Basic.Util.to_string + in + Pgn2tex.MoveMap.add move_int fen_str acc) + Pgn2tex.MoveMap.empty json_list + +let convert_js pgn_js diagram_json_js display_clock = + let pgn = Js.to_string pgn_js in + + (* convert JS string into OCaml string *) + let json_str = Js.to_string diagram_json_js in + + let diagram_data = + if json_str = "" || json_str = "{}" then Pgn2tex.MoveMap.empty + else parse_diagram_json json_str + in + + let result = Pgn2tex.to_tex pgn ~diagram_data ~clock:display_clock in + Js.string result + +(* Exporting the module to the global JavaScript scope *) +let () = Js.export "to_tex" (Js.wrap_callback convert_js) diff --git a/dune-project b/dune-project index 243cf0e..b941d01 100644 --- a/dune-project +++ b/dune-project @@ -1,5 +1,7 @@ (lang dune 3.21) +(using menhir 3.0) + (name pgn_to_tex) (generate_opam_files true) @@ -11,9 +13,11 @@ (maintainers "Owen Rees ") -(license AGPLv3) +(license AGPL-3.0-or-later) -(documentation https://url/to/documentation) +(documentation https://github.com/TheRealOwenRees/pgn_to_tex/README.md) + +(version 0.0.1) (package (name pgn_to_tex) diff --git a/lib/ast.ml b/lib/ast.ml new file mode 100644 index 0000000..6967564 --- /dev/null +++ b/lib/ast.ml @@ -0,0 +1,14 @@ +module MoveMap = Map.Make (Int) + +type tag = { key : string; value : string } + +type item = + | Move of string + | Number of string + | Clock of string + | Nag of string + | Comment of string + | Result of string + | Variation of item list + +type game = { tags : tag list; content : item list; result : string option } diff --git a/lib/dune b/lib/dune index 46be8db..9306f91 100644 --- a/lib/dune +++ b/lib/dune @@ -1,2 +1,10 @@ (library - (name pgn_to_tex)) + (name pgn_logic) + (public_name pgn_to_tex) + (libraries sedlex menhirLib) + (modules ast parser lexer parsing latex latex_helpers pgn2tex nag) + (preprocess + (pps sedlex.ppx))) + +(menhir + (modules parser)) diff --git a/lib/latex.ml b/lib/latex.ml new file mode 100644 index 0000000..2bfd2a2 --- /dev/null +++ b/lib/latex.ml @@ -0,0 +1,115 @@ +open Ast +open Latex_helpers + +(* convert PGN headers to title tex *) +let tags_to_tex tags = + let get_tag_value key tags ~default = + match List.find_opt (fun t -> t.key = key) tags with + | Some t -> t.value + | None -> default + in + + let white = get_tag_value "White" tags ~default:"" in + let black = get_tag_value "Black" tags ~default:"" in + let event = get_tag_value "Event" tags ~default:"" in + let date = get_tag_value "Date" tags ~default:"" in + let white_elo = get_tag_value "WhiteElo" tags ~default:"" in + let black_elo = get_tag_value "BlackElo" tags ~default:"" in + let site = get_tag_value "Site" tags ~default:"" in + let title = get_tag_value "Title" tags ~default:"" in + let subtitle = get_tag_value "Subtitle" tags ~default:"" in + let author = get_tag_value "Author" tags ~default:"" in + + let title_tex = build_title_string event title subtitle in + let author_tex = build_author_string author white black white_elo black_elo in + + title_tex ^ "\n" ^ author_tex ^ "\n" + ^ build_date_site_string date site + ^ "\\maketitle\n\\newchessgame" + +let rec item_to_tex is_mainline ply ?(diagram_data = MoveMap.empty) = function + | Number n -> if is_mainline then "\\textbf{" ^ n ^ "}" else n + | Move m -> + if is_mainline then "\\textbf{" ^ escape_tex m ^ "}" else escape_tex m + | Comment c -> + if is_mainline then "\\newline " ^ escape_tex c ^ "\\par" + else escape_tex c + | Result r -> "\\textbf{" ^ r ^ "}" + | Variation v -> "( " ^ render_game false ~diagram_data v ^ " )" + | Nag g -> " " ^ Nag.nag_to_tex g ^ "{}" + | Clock c -> c + +and render_game is_mainline ?(diagram_data = MoveMap.empty) ?(clock = false) + items = + let rec aux ply interrupted ?(white_time = "0:00") ?(black_time = "0:00") = + function + | [] -> "" + | Comment c :: Move m :: tail when is_mainline && ply mod 2 != 0 -> + let next_ply = ply + 1 in + let rendered_comment = "\\newline " ^ escape_tex c ^ "\\par" in + let rendered_move = "\\textbf{\\ldots{}" ^ escape_tex m ^ "}" in + let diag_str = + match get_diagram next_ply diagram_data with + | Some s -> s + | None -> "" + in + let has_diag = diag_str <> "" in + rendered_comment ^ " " ^ rendered_move ^ diag_str ^ " " + ^ aux next_ply has_diag tail + | head :: tail -> ( + match head with + | Clock c -> + if ply mod 2 = 0 then + aux ply interrupted ~white_time ~black_time:c tail + else aux ply interrupted ~white_time:c ~black_time tail + | _ -> + let is_move = match head with Move _ -> true | _ -> false in + + let next_ply = + match head with Move _ when is_mainline -> ply + 1 | _ -> ply + in + + let prefix = + if is_move && is_mainline && next_ply mod 2 == 0 && interrupted + then "\\ldots{}" + else "" + in + + let rendered_head = + let raw_head = + item_to_tex is_mainline next_ply ~diagram_data head + in + if prefix <> "" && raw_head <> "" then + "\\textbf{" ^ prefix ^ "}" ^ raw_head + else raw_head + in + + let diag_str = + if is_move && is_mainline then + get_diagram next_ply diagram_data ~clock ~white_time ~black_time + else None + in + + let diag_output = match diag_str with Some s -> s | None -> "" in + let is_comment = match head with Comment _ -> true | _ -> false in + let next_interrupted = diag_output <> "" || is_comment in + let rest = + aux next_ply next_interrupted ~white_time ~black_time tail + in + + if rendered_head = "" then rest + else + rendered_head ^ diag_output ^ if rest = "" then "" else " " ^ rest + ) + in + String.trim (aux 0 false items) + +let game_to_tex game ~diagram_data ~clock = + let header_tex = tags_to_tex game.tags in + let content_tex = render_game true ~diagram_data ~clock game.content in + + let result_tex = + match game.result with Some r -> " \\textbf{" ^ r ^ "}" | None -> "" + in + + header_tex ^ "\n" ^ content_tex ^ result_tex diff --git a/lib/latex_helpers.ml b/lib/latex_helpers.ml new file mode 100644 index 0000000..a6923d6 --- /dev/null +++ b/lib/latex_helpers.ml @@ -0,0 +1,63 @@ +module MoveMap = Ast.MoveMap + +(* escape strings for LaTeX *) +let escape_tex s = + let b = Buffer.create (String.length s) in + String.iter + (fun c -> + match c with + | '#' -> Buffer.add_string b "\\#" + | '%' -> Buffer.add_string b "\\%" + | '{' -> Buffer.add_string b "\\{" + | '}' -> Buffer.add_string b "\\}" + | '_' -> Buffer.add_string b "\\_" + | '&' -> Buffer.add_string b "\\&" + | _ -> Buffer.add_char b c) + s; + Buffer.contents b + +(* ELO string builder *) +let build_elo_string elo = if String.length elo > 0 then "(" ^ elo ^ ")" else "" + +(* Author string builder *) +let build_author_string author white black white_elo black_elo = + match (author, white, black) with + | "", "", "" -> "\\author{}" + | "", white, black -> + "\\author{" ^ escape_tex white ^ " " ^ build_elo_string white_elo ^ "\\\\" + ^ escape_tex black ^ " " ^ build_elo_string black_elo ^ "}" + | author, "", "" -> "\\author{" ^ escape_tex author ^ "}" + | _ -> "" + +(* Title string builder *) +let build_title_string event title subtitle = + match (event, title, subtitle) with + | event, "", "" -> "\\title{" ^ escape_tex event ^ "}" + | _event, title, subtitle -> + "\\title{" ^ title ^ "}\\\\[2ex]\\large{" ^ subtitle ^ "}" + +(* Date and Site string builder *) +let build_date_site_string date site = + match (date, site) with + | "", "" -> "\\date{}" + | "", s -> "\\date{" ^ s ^ "}" + | d, "" -> "\\date{" ^ d ^ "}" + | d, s -> "\\date{" ^ d ^ ", " ^ s ^ "}" + +(* generate a diagram with or without clock times from the passed in Map (JSON in JS) *) +let get_diagram ?(clock = false) ?(white_time = "0:00") ?(black_time = "0:00") + ply diagram_data = + match MoveMap.find_opt ply diagram_data with + | None -> None + | Some fen -> + if not clock then + Some + ("\n\\par\\nobreak\\medskip\\chessboard[setfen=" ^ fen + ^ ", vmargin=false]\\par\\medskip\n") + else + Some + ("\\par\\medskip\\noindent" ^ "\\begin{minipage}{\\linewidth}" + ^ black_time ^ "\\par\\nopagebreak\\smallskip\n" + ^ "\\chessboard[setfen=" ^ fen + ^ ", vmargin=false]\\par\\nopagebreak\\vspace{1em}\n" ^ white_time + ^ "\\end{minipage}\\par\\medskip") diff --git a/lib/lexer.ml b/lib/lexer.ml new file mode 100644 index 0000000..3c7bfd2 --- /dev/null +++ b/lib/lexer.ml @@ -0,0 +1,103 @@ +open Sedlexing +open Parser + +let digit = [%sedlex.regexp? '0' .. '9'] +let letter = [%sedlex.regexp? 'a' .. 'z' | 'A' .. 'Z'] +let alphabetic = [%sedlex.regexp? letter] +let move_char = [%sedlex.regexp? letter | digit | '+' | '#' | '=' | '-' | 'x'] + +let piece_square_char = + [%sedlex.regexp? 'K' | 'Q' | 'R' | 'B' | 'N' | 'O' | 'a' .. 'h'] + +let clock_val = [%sedlex.regexp? Plus (digit | ':'), Opt ('.', Plus digit)] + +let rec tokenize_header buf = + match%sedlex buf with + | Plus white_space -> tokenize_header buf + | ']' -> TAG_CLOSE + | '"' -> read_string (Buffer.create 16) buf + | letter, Star (letter | digit | '_') -> HEADER (Utf8.lexeme buf) + | eof -> EOF + | _ -> failwith "Unexpected character in header" + +and tokenize_game buf = + match%sedlex buf with + | Plus white_space -> tokenize_game buf + | "[%clk " -> read_inline_clock buf + | '[' -> TAG_OPEN + | '{' -> read_comment (Buffer.create 32) buf + | '(' -> LPAREN + | ')' -> RPAREN + | '$', Plus digit -> NAG (Utf8.lexeme buf) + | Plus digit, Plus '.' -> NUMBER (Utf8.lexeme buf) + | "1-0" | "0-1" | "1/2-1/2" | "*" -> RESULT (Utf8.lexeme buf) + | "O-O" | "O-O-O" -> MOVE (Utf8.lexeme buf) + | ('K' | 'Q' | 'R' | 'B' | 'N' | 'a' .. 'h'), Star move_char -> + MOVE (Utf8.lexeme buf) + | eof -> EOF + | any -> tokenize_game buf + | _ -> failwith "Unexpected character in game" + +and read_string b buf = + match%sedlex buf with + | '"' -> STRING (Buffer.contents b) + | '\\', '"' -> + Buffer.add_char b '"'; + read_string b buf + | any -> + Buffer.add_string b (Utf8.lexeme buf); + read_string b buf + | _ -> failwith "Unterminated string" + +and read_inline_clock buf = + match%sedlex buf with + | clock_val -> + let time = Utf8.lexeme buf in + begin match%sedlex buf with + | ']' -> CLOCK time + | _ -> failwith "Malformed inline clock: expected closing ']'" + end + | _ -> failwith "Malformed inline clock value" + +and read_comment b buf = + match%sedlex buf with + | Plus white_space -> read_comment b buf + | "[%clk " -> read_comment_clock buf + | "[%" -> skip_bracket_tag buf b + | '}' -> COMMENT (Buffer.contents b) + | eof -> failwith "Unterminated comment" + | any -> + Buffer.add_string b (Utf8.lexeme buf); + read_comment_content b buf + | _ -> failwith "Malformed comment" + +and read_comment_clock buf = + match%sedlex buf with + | clock_val -> + let time = Utf8.lexeme buf in + begin match%sedlex buf with + | ']' -> begin + match%sedlex buf with + | Star white_space, '}' -> CLOCK time + | _ -> failwith "Expected closing '}' after clock comment" + end + | _ -> failwith "Malformed clock in comment: expected ']'" + end + | _ -> failwith "Malformed clock value" + +and read_comment_content b buf = + match%sedlex buf with + | "[%" -> skip_bracket_tag buf b + | '}' -> COMMENT (String.trim (Buffer.contents b)) + | eof -> failwith "Unterminated comment" + | any -> + Buffer.add_string b (Utf8.lexeme buf); + read_comment_content b buf + | _ -> failwith "Malformed comment" + +and skip_bracket_tag buf b = + match%sedlex buf with + | ']' -> read_comment_content b buf + | eof -> failwith "Unterminated [% tag inside comment" + | any -> skip_bracket_tag buf b + | _ -> failwith "Malformed comment" diff --git a/lib/nag.ml b/lib/nag.ml new file mode 100644 index 0000000..daf05c0 --- /dev/null +++ b/lib/nag.ml @@ -0,0 +1,32 @@ +let nag_to_tex nag = + match nag with + (* Move Assessments *) + | "$1" -> "!" (* Good move *) + | "$2" -> "?" (* Poor move *) + | "$3" -> "!!" (* Very good move *) + | "$4" -> "??" (* Very poor move *) + | "$5" -> "!?" (* Speculative move *) + | "$6" -> "?!" (* Questionable move *) + (* | 7 -> "\\Box" Forced move *) + (* | 8 -> "{\\tiny \\textceltel}" Singular move *) + (* | 9 -> "{\\tiny !!}" Worst move *) + (* Positional Assessments *) + | "$10" -> "=" (* Drawish / Equal *) + (* | 11 -> "=" Equal/Quiet *) + | "$13" -> "\\infty" (* Unclear *) + | "$14" -> "\\wbetter" (* White slight advantage *) + | "$15" -> "\\bbetter" (* Black slight advantage *) + | "$16" -> "\\ensuremath{\\pm}" (* White moderate advantage *) + | "$17" -> "\\ensuremath{\\mp}" (* Black moderate advantage *) + | "$18" -> "+-" (* White decisive advantage *) + | "$19" -> "-+" (* Black decisive advantage *) + (* Common Positional Symbols *) + | "$22" -> "\\zugzwang" + | "$32" -> "\\development" + | "$36" -> "\\initiative" + | "$40" -> "\\attack" + | "$44" -> "\\compensation" + | "$132" -> "\\counterplay" + (* | 146 -> "N" Novelty *) + (* Fallback for others: just print the $ number as a comment or ignore *) + | n -> "" diff --git a/lib/parser.mly b/lib/parser.mly new file mode 100644 index 0000000..bb21c6a --- /dev/null +++ b/lib/parser.mly @@ -0,0 +1,35 @@ +%{ + open Ast +%} + +%token TAG_OPEN TAG_CLOSE LPAREN RPAREN EOF +%token HEADER STRING MOVE NUMBER COMMENT RESULT CLOCK NAG + +%start main + +%% + +main: + | ts = tags; c = content; r = option(RESULT); EOF + { { tags = ts; content = c; result = r } } + +tags: + | TAG_OPEN; k = HEADER; v = STRING; TAG_CLOSE; rest = tags + { { key = k; value = v } :: rest } + | (* empty *) + { [] } + +content: + | i = item; rest = content + { i :: rest } + | (* empty *) + { [] } + +item: + | m = MOVE { Move m } + | n = NUMBER { Number n } + | c = CLOCK { Clock c } + | g = NAG { Nag g } + | c = COMMENT { Comment c } + | r = RESULT { Result r } + | LPAREN; v = content; RPAREN { Variation v } diff --git a/lib/parsing.ml b/lib/parsing.ml new file mode 100644 index 0000000..fade544 --- /dev/null +++ b/lib/parsing.ml @@ -0,0 +1,42 @@ +open Ast + +let in_header = ref false + +let next_token buf = + if !in_header then + let tok = Lexer.tokenize_header buf in + match tok with + | Parser.TAG_CLOSE -> + in_header := false; + Parser.TAG_CLOSE + | _ -> tok + else + let tok = Lexer.tokenize_game buf in + match tok with + | Parser.TAG_OPEN -> + in_header := true; + Parser.TAG_OPEN + | _ -> tok + +let parse_pgn s = + let buf = Sedlexing.Utf8.from_string s in + in_header := false; + let provider () = + let tok = next_token buf in + let start_pos, end_pos = Sedlexing.lexing_positions buf in + (tok, start_pos, end_pos) + in + try MenhirLib.Convert.Simplified.traditional2revised Parser.main provider + with _ -> failwith "Parse error" + +(* let parse_diagram_json json_str = + let json = Yojson.Basic.from_string json_str in + let json_assoc = Yojson.Basic.Util.to_assoc json in + + List.fold_left + (fun acc (key_str, json_value) -> + let value_string = Yojson.Basic.Util.to_string json_value in + match int_of_string_opt key_str with + | Some k -> Pgn2tex.MoveMap.add k value_string acc + | None -> acc) + Pgn2tex.MoveMap.empty json_assoc *) diff --git a/lib/pgn2tex.ml b/lib/pgn2tex.ml new file mode 100644 index 0000000..24dc4bc --- /dev/null +++ b/lib/pgn2tex.ml @@ -0,0 +1,5 @@ +module MoveMap = Ast.MoveMap + +let to_tex pgn ~diagram_data ~clock = + let game = Parsing.parse_pgn pgn in + Latex.game_to_tex game ~diagram_data ~clock diff --git a/pgn_to_tex.opam b/pgn_to_tex.opam index 3ab1729..d74c524 100644 --- a/pgn_to_tex.opam +++ b/pgn_to_tex.opam @@ -1,14 +1,15 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" +version: "0.0.1" synopsis: "A chess PGN to TeX conversion tool" description: "Convert PGN files to LaTeX markdown, including rendered images of selected positions" maintainer: ["Owen Rees "] authors: ["Owen Rees "] -license: "AGPLv3" +license: "AGPL-3.0-or-later" tags: ["pgn" "latex" "chess" "parser"] homepage: "https://github.com/therealowenrees/pgn_to_tex" -doc: "https://url/to/documentation" +doc: "https://github.com/TheRealOwenRees/pgn_to_tex/README.md" bug-reports: "https://github.com/therealowenrees/pgn_to_tex/issues" depends: [ "dune" {>= "3.21"} diff --git a/test/dune b/test/dune index d4e693b..086c88b 100644 --- a/test/dune +++ b/test/dune @@ -1,3 +1,6 @@ (test - (name test_pgn_to_tex) - (libraries pgn_to_tex)) + (name test_suite) + (modules (:standard)) + (libraries ounit2 sedlex sedlex.ppx pgn_to_tex helpers) + (deps + (source_tree pgn_examples))) diff --git a/test/helpers/dune b/test/helpers/dune new file mode 100644 index 0000000..1ec175c --- /dev/null +++ b/test/helpers/dune @@ -0,0 +1,3 @@ +(library + (name helpers) + (modules latex_helper)) diff --git a/test/helpers/latex_helper.ml b/test/helpers/latex_helper.ml new file mode 100644 index 0000000..325c94e --- /dev/null +++ b/test/helpers/latex_helper.ml @@ -0,0 +1,12 @@ +module MoveMap = Map.Make (Int) + +let diagram_data = + MoveMap.empty + |> MoveMap.add 5 + "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3" + |> MoveMap.add 6 + "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 3" + |> MoveMap.add 10 + "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 3 5" + |> MoveMap.add 15 + "r1bqkb1r/pp1ppppp/2n5/2p5/4P3/2N2N2/PPPP1PPP/R1BQKB1R b KQkq - 5 7" diff --git a/test/pgn_examples/1.pgn b/test/pgn_examples/1.pgn new file mode 100644 index 0000000..2ea0a1a --- /dev/null +++ b/test/pgn_examples/1.pgn @@ -0,0 +1,15 @@ +[Event "URS-chJ"] +[Site "Kherson"] +[Date "1991.??.??"] +[Round "?"] +[White "Ibragimov, Ildar"] +[Black "Kramnik, Vladimir"] +[Result "0-1"] +[ECO "A88"] +[WhiteElo "2455"] +[BlackElo "2480"] +[PlyCount "110"] +[EventDate "1991.??.??"] +[Source "ChessBase"] + +1. d4 {A88: Dutch Defence: Leningrad System: 5 Nf3 0-0 6 0-0 d6 7 Nc3 c6} d6 2. c4 f5 3. Nf3 Nf6 4. g3 g6 5. Bg2 Bg7 6. O-O O-O 7. Nc3 Qe8 8. b3 Na6 9. Ba3 c6 10. Qd3 Rb8 11. e4 fxe4 12. Nxe4 Bf5 13. Nxf6+ Bxf6 14. Qd2 Nc7 15. Rae1 Qd7 16. h4 b5 17. Re3 bxc4 18. bxc4 Bh3 19. Rfe1 Bxg2 {last book move} 20. Kxg2 Qf5 21. Re4 Rbe8 22. Rf4 Qc8 23. Qa5 d5 24. cxd5 Nxd5 25. Rfe4 {e7 seems the pivot of the position} Qf5 26. Qd2 ( 26. Qxa7$4 {taking the pawn is naive} Qxf3+ {Annihilates a defender: f3} 27. Kxf3 Bxd4+ 28. Kg4 Bxa7 29. Bxe7 Rxf2$19 ( 29... Rxe7 30. Rxe7 Bxf2 31. a4$19 ) ( 29... Nxe7$6 30. Rxe7 Rxe7 31. Rxe7 Bxf2 32. a4$19 ) ) Bg7 27. Nh2 Rf7 28. Bc5 {The white bishop on an outpost} Qd7 29. a4 Nf6 30. Re5 Nd5 31. R5e4 ( 31. R5e2 Rb8$14 ) Nf6$11 32. Re5 Kh8 ( 32... Nd5 33. R5e2$14 ) 33. Kg1 Nd5 {A valuable piece} 34. R5e2 a6 35. Qd3 Qh3 ( 35... Ra8$14 ) 36. Nf3 ( 36. Qxa6 Ref8 37. Qa5$14 ( 37. Qxc6 Rxf2 38. Rxf2 Qxg3+ 39. Kh1 Rxf2 ( 39... Qxf2$6 40. Rf1 Qa2 41. Rxf8+ Bxf8 42. Kg1$11 ) 40. Qa8+ Bf8 41. Qxf8+ Rxf8 42. Re2 Rf2 43. Rxf2 Qxf2 44. Ng4 Qf1+ 45. Kh2 Nf4 46. Ne3 Qf2+ 47. Kh1 Qxh4+ 48. Kg1 Qg3+ 49. Kf1 Qxe3 50. Bb6 Qe2+ 51. Kg1 Qg2# ) ( 37. Bxe7$4 {White will choke on that pawn} Rxf2 ( 37... Nxe7$6 38. Rd1$19 ) 38. Rxf2 Qxg3+ 39. Kh1 Rxf2 ( 39... Qxf2 40. Re2 Qxd4 41. Bxf8 Qd1+ 42. Kg2 Nf4+ 43. Kf3$11 ) 40. Qc8+ Bf8 41. Bf6+ Nxf6 42. Qxf8+ Ng8 43. Qxf2 Qxf2$19 ) ) Nf4$15 {Do you see the mate threat?} 37. gxf4 Rxf4 {The mate threat is Rg4} 38. Ne5 ( 38. Re4$5 {is worthy of consideration} Qg4+ 39. Kf1 Rxf3 40. Rxg4 Rxd3 41. Re6$15 ) Rg4+$1$17 {keeping the advantage} 39. Nxg4 {Theme: Deflection from d3} Qxd3 40. Re4 Qf3 41. Nh2 Qf5 42. Bxe7 ( 42. Kg2 Rf8 43. f3 Bf6$19 ) Kg8 43. f3 ( 43. R1e3 Qd5$19 ) Qh3$19 44. R1e2 ( 44. Kh1$19 ) Qg3+ 45. Rg2 Bxd4+$1 {a devastating blow} 46. Kh1 ( 46. Rxd4 {A deflection} Qe1+ {Theme: Double Attack} ) Qh3 47. Rgg4 ( 47. a5 Bc3$19 ) c5 48. h5 ( 48. Re1 {does not improve anything} Bf6 49. Rge4 Bxe7 50. Rxe7 Rxe7 51. Rxe7 Qxh4$19 ) Rb8 49. Re1 Be5 50. Rh4 Qf5 51. hxg6 hxg6 52. Re2 ( 52. Bxc5 {doesn't change the outcome of the game} Bg3 53. Rb4 Rc8$19 ( 53... Bxe1$6 {is clearly worse} 54. Rxb8+ Kh7 55. Rb7+ Kh8 56. Rb8+ Kg7 57. Rb7+ Kf6 58. Ng4+ Kg5 59. Be3+ Kh4 60. Kg2 Qc2+ 61. Nf2$15 ) ( 53... Qxc5$6 {is much worse} 54. Rxb8+ Bxb8 55. Re8+ Kf7 56. Rxb8$19 ) ) Rb1+ ( 52... Qb1+$5 {keeps an even firmer grip} 53. Kg2 Rb2 54. Rxb2 Qxb2+ 55. Kg1$19 ) 53. Kg2 Bd4 54. Ng4 ( 54. Rxd4 {no good, but what else?} cxd4 55. Ng4$19 ) Rg1+ 55. Kh2 ( 55. Kh3 {doesn't do any good} Qxf3+ 56. Kh2 Rh1# ) Qf4+ ( 55... Qf4+ 56. Kh3 Qg3# ) 0-1 diff --git a/test/pgn_examples/10.pgn b/test/pgn_examples/10.pgn new file mode 100644 index 0000000..50ccd2d --- /dev/null +++ b/test/pgn_examples/10.pgn @@ -0,0 +1,13 @@ +[Event "Grob: Grob Opening- Zilbermints Gambit"] +[Result "*"] +[Variant "Standard"] +[ECO "A00"] +[Opening "Grob Opening: Zilbermints Gambit"] +[StudyName "Grob"] +[ChapterName "Grob Opening- Zilbermints Gambit"] +[ChapterURL "https://lichess.org/study/UEerNKLR/1C61RNns"] +[Annotator "https://lichess.org/@/Billy_Sprinkle"] +[UTCDate "2025.07.08"] +[UTCTime "10:35:06"] + +1. g4 (1. e4 d5 2. g4 dxe4 3. Nc3 { A possible Scandi reply. }) 1... d5 2. e4 { 2.e4!? a very dubious move which the engine will cry at you for, but is an interesting possibility for white. I included this as its own chapter for, if white wishes they can use this line as a cheeky Scandi reply. 1.e4 d5 2.g4. } 2... dxe4 { A move that white will see 81% of the time. } (2... d4 { Black says: "No thank you; I do not wish to engage in your shenanigans." } 3. Bc4 { I recommond this move, for its similar to the ideas on the Englund gambit if white refused to take. The bishop becomes alive and blood lusting after the f7 square. } { [%csl Gf7][%cal Gc4f7] } 3... e5 4. d3 Be7 5. Qf3 { [%cal Gg1e2,Gf3g3] } 5... Be6 6. Bxe6 fxe6 7. Qg3 { Side stepping the queen to interesting square. } 7... Nc6 8. Nf3 { [%csl Gb4] }) (2... Nf6 3. e5 Nxg4 4. d4 Bf5 5. Bd3 Bxd3 6. Qxd3 { White is down a pawn, and does not have sufficient compensation for it. Nevertheless, play will continue and its up to white to try and squeeze blood out of a stone. The blood tole for playing dubious openings may lead to one losing a pawn or two now and then. }) 3. Nc3 { Officially in the Zilbermints Gambit, which plays very similar to the Englund gambit. } 3... Nf6 { Black's common move which hopes to defend the pawn whilst also developing a piece. } (3... e5 4. d3 { 4.d3 is an important move to understand the idea behind. For this is Zilbermints Hartlaub gambit, too which there is a similar line in the Englund called the Hartlaub-Charlick gambit (1.d4 e5 2.dxe5 d6) this gambit complex is quite fun and effective at faster time controls, and I recommond the chessable course "Pressure 1.d4: Hartlaub-Charlick Gambit for Black" by Russ Southern to getter a better understanding of key patterns and themes for this gambit complex. } { [%cal Gf1d3,Gd1e2,Gg1f3,Gc1g5,Ge1c1] } 4... exd3 5. Bxd3 Nc6 6. Qe2 { At this point there are numerous possibilities for black, so take these lines as an expression of the general ideas that can occur in these positions. } 6... Be6 7. h3 Qd7 8. Nf3 f6 9. Be3 O-O-O 10. O-O-O { Black as seen to have secured his strong centre with active pieces in the middle. } { [%csl Gd3] } 10... Bb4 { [%cal Gd1d7,Gd3b5] } 11. Bb5 { Exposing the Queen to the rook on d1. } 11... Qe7 { The queen steps out the way and tactics will follow. } 12. Bxc6 Rxd1+ 13. Rxd1 Bxc3 14. Qb5 { 14.Bxb7 was played in the game by Zilbermints (cite xxxx), but the text move is way stronger leading to a mate attack. } { [%cal Gc3b2] } 14... bxc6 { Black can prolong there suffering with 14...Bxb2, too which 15.Kb1 Bxa2 16.Kxb2 Qb4 17.Qxb4. However, there is no need to commit to learning such a sequence. } 15. Qa6+ Kb8 16. Qxa7+ Kc8 17. Qa8#) (3... Nc6 { An annoying move by black, for it prevents d3 followed by Bxd3 ideas for black has Nb4 and even possibly Bxg4 deflecting the queen from defence of the c2 square. } { [%cal Gc6b4,Gb4d3,Gb4c2,Gc8g4] } 4. Bg2 e5 5. h3 h5 { A quite painful move white has to endure. } 6. gxh5 Qh4 (6... f5 7. d3 Nf6 8. Bg5 Bb4 9. Ne2 exd3 10. Bxc6+ bxc6 11. cxd3 { This is unpleasant for white, for in these Englund gambit positions you want to avoid creating the isolated e pawn. } { [%csl Gd3,Gf2,Gb2,Ga2] }) (6... Be6 7. Bxe4 Qd7 8. d3 O-O-O 9. Bxc6 Qxc6 10. Qf3 Qxf3 11. Nxf3 { It could be argued that it is unwise for white to trade down a dozen pieces when in a bad position. But due to the cramped nature of white position, whites pieces will be bumping into each other anyways. As a result, the mass exchange affords white some space to move their pieces. } 11... f6 12. Be3 Rxh5 { Though material is equal black is much better due to the bishop pair and the clearer pawn structure. But, white can still throw punches, and therefore there is hope. }) 7. Nxe4 Qxh5 { Trading of queens is not the right idea for black, for it gives up all of the pressure black was exerting. } 8. Qxh5 Rxh5 9. Ng3 { White has survived, black is a bit better but it could of been worse. }) (3... f5 { Black attempts to defend the pawn. } 4. d3 { Continuing with the gambiting idea. } 4... Nc6 5. Be3 exd3 6. Bxd3 Nb4 7. Bb5+ c6 8. Qxd8+ Kxd8 9. O-O-O+ Ke8 10. a3 cxb5 11. axb4 { The position is playable. } { [%cal Gc3b5,Gb5c7] }) 4. g5 { White kicks away the knight. } 4... Nd5 (4... Bg4 { A in-between move in which Black hopes to increase the pressure on the kingside. } 5. Be2 { Offering the exchange of bishops which will lead to an active placed queen. } 5... Bxe2 6. Qxe2 { This now looks very similar to the traditional Englund gambit. } 6... Nd5 7. Qb5+ { [%cal Gb5d5,Gb5b7] } 7... c6 8. Qxb7 Nd7 9. Nxd5 { Exchanging of the knights which at the end of the sequence white will be up two pawns. } 9... cxd5 10. Qxd5 Rc8 11. Qxe4 Nc5 12. Qe3 Qd5 13. Nf3 { [%csl Gf3] }) 5. Qe2 { 5.Qe2 perhaps brings the most attacking grit for white, for white intends Qb5. } { [%cal Ge2b5,Gb5b7,Gb5d5] } (5. Nxe4 Bf5 6. Ng3 Bg6 7. h4 h5 8. Bg2 { With N1e2 and then 0-0, white will then have the task of just playing chess. }) 5... Nxc3 { Not the strongest move, but the one played the most. } (5... Nb4 6. Qxe4 Qd4 7. Bg2) 6. dxc3 Bf5 7. Qb5+ Qd7 { A key error, black plays this, I imagine, for the hope that queen trades are forced. } (7... Bd7 { Retreating moves are perhaps one of the hardest moves to see, but this leads to 0.0 according to the engine. } 8. Qxb7 Nc6 9. Qa6 { Overall, I like white chance, for white has intuitive squares to place their pieces and from there white will play on. } { [%cal Gc1e3,Ge1c1,Gg1e2,Gf1g2] }) 8. Qxb7 Qc6 { Trying to defend the rook. } (8... Nc6 9. Qxa8+) (8... Qc8 9. Qxa8) 9. Bb5 { Too which, it fails miserably. } * diff --git a/test/pgn_examples/11.pgn b/test/pgn_examples/11.pgn new file mode 100644 index 0000000..1116a5d --- /dev/null +++ b/test/pgn_examples/11.pgn @@ -0,0 +1,18 @@ +[Event "Grob: Introduction."] +[Result "*"] +[Variant "Standard"] +[ECO "A00"] +[Opening "Grob Opening: Grob Gambit, Fritz Gambit"] +[StudyName "Grob"] +[ChapterName "Introduction."] +[ChapterURL "https://lichess.org/study/UEerNKLR/35Ir8bOE"] +[Annotator "https://lichess.org/@/Billy_Sprinkle"] +[UTCDate "2025.04.26"] +[UTCTime "14:44:02"] + +{ Basman, Bloodgood } +1. g4 { This is where white has to make a decision between 2.Bg2 (the tactical Grob) or 2.h3 (the positional Grob). Bishop g2 is the move which will be presented through out this course, for the desire is to gambit the g4 in order to distract blacks queens bishop from the defence of the queenside. Moves c4 Qb3 is the general plan. Where as h3 will lead to its own type of game, known as the positional Grob. Too which, I would highly recommond U Cannot Be Serious! by Basman & Welling, and The Killer Grob by Basman. Its most persistent practitioner was IM Mike Basman (this maybe better in the intro) } 1... d5 { [%cal Gh2h3] } 2. Bg2 Bxg4 { [%csl Gg4] } 3. c4 c6 (3... d4) 4. cxd5 { [%csl Gg4] } (4. Qb3 Qc7 5. cxd5 cxd5 (5... e6 6. h3 Bh5 7. e4 Bg6 8. dxe6 fxe6 9. Qxe6+) 6. Nc3 d4 (6... e6 7. Qa4+ { [%cal Ga4g4,Ga4e8] }) (6... Nc6 7. Nxd5 Qd7 8. Qa4 Rc8 9. d3 { [%csl Ge7] } 9... e5 (9... e6 10. Qxg4 exd5 11. Qxd7+ Kxd7 12. Bh3+) 10. Bd2 Nd4 11. Qxd7+ Bxd7 12. Kd1) 7. Qxb7 (7. Nb5 Qb6 8. Bxb7 { [%cal Gb6b7,Gb5d6] } 8... Qxb7 (8... Be6 9. Qf3 Qxb5 10. Bxa8 Nf6 11. Qb7) 9. Nd6+ exd6 10. Qxb7) 7... Qxb7 8. Bxb7 dxc3 9. dxc3 a5 10. Bxa8) 4... Nf6 (4... Qc7 5. Nc3 Nf6 6. h3 (6. Qb3 e6 7. h3 Bh5 8. dxe6) 6... Bd7 7. e4 e6 8. dxe6 Bxe6 9. d4 Nbd7 10. Nge2 g6 11. Be3 Bg7 12. O-O O-O 13. Rb1) (4... cxd5 5. Qb3 e6 (5... Nf6) 6. Qa4+ Nc6 7. Qxg4) 5. Qb3 Qc7 { [%csl Gg4] } 6. Nc3 e6 7. h3 Bh5 8. dxe6 fxe6 9. Qxe6+ { Refrences: unorthodoxchessopenings.com +The Tactical Grob- Cluade Bloodgood +The killer Grob- Michael Basman +Basman's folly- +U cannot be serious } * diff --git a/test/pgn_examples/2.pgn b/test/pgn_examples/2.pgn new file mode 100644 index 0000000..17ba4d5 --- /dev/null +++ b/test/pgn_examples/2.pgn @@ -0,0 +1,18 @@ +[Event "Casual Rapid game"] +[Site "https://lichess.org/9Z0taHcG"] +[Date "2023.12.03"] +[White "montillo2015"] +[Black "reassessyourchess"] +[Result "1-0"] +[UTCDate "2023.12.03"] +[UTCTime "19:57:32"] +[WhiteElo "1919"] +[BlackElo "1869"] +[Variant "Standard"] +[TimeControl "600+3"] +[ECO "D05"] +[Opening "Queen's Pawn Game: Colle System"] +[Termination "Normal"] +[Annotator "lichess.org"] + +1. d4 { [%clk 0:10:00] } 1... Nf6 { [%clk 0:10:00] } 2. Nf3 { [%clk 0:10:00] } 2... d5 { [%clk 0:10:01] } 3. e3 { [%clk 0:10:01] } 3... e6 { [%clk 0:10:02] } { D05 Queen's Pawn Game: Colle System } 4. a3 { [%clk 0:10:01] } 4... c5 { [%clk 0:10:02] } 5. c4 { [%clk 0:10:02] } 5... Nc6 { [%clk 0:10:04] } 6. dxc5 { [%clk 0:10:03] } 6... Bxc5 { [%clk 0:10:01] } 7. b4 { [%clk 0:10:03] } 7... Be7 { [%clk 0:10:00] } 8. Bb2 { [%clk 0:10:04] } 8... dxc4 { [%clk 0:09:56] } 9. Qxd8+ { [%clk 0:10:04] } 9... Nxd8 { [%clk 0:09:57] } 10. Bxc4 { [%clk 0:10:05] } 10... O-O { [%clk 0:09:56] } 11. O-O { [%clk 0:10:06] } 11... a6 { [%clk 0:09:55] } 12. Nc3 { [%clk 0:09:51] } 12... b5 { [%clk 0:09:52] } 13. Be2 { [%clk 0:09:42] } 13... Bb7 { [%clk 0:09:51] } 14. Nd4 { [%clk 0:09:27] } 14... Ne4 { [%clk 0:09:41] } 15. Nxe4 { [%clk 0:09:23] } 15... Bxe4 { [%clk 0:09:42] } 16. Bf3 { [%clk 0:09:24] } 16... Bxf3 { [%clk 0:09:37] } 17. Nxf3 { [%clk 0:09:24] } 17... Nc6 { [%clk 0:09:39] } 18. Rac1 { [%clk 0:09:18] } 18... Rac8 { [%clk 0:09:40] } 19. Rfd1 { [%clk 0:09:19] } 19... Rfd8 { [%clk 0:09:09] } 20. Rxd8+ { [%clk 0:09:14] } 20... Rxd8 { [%clk 0:09:10] } 21. g3 { [%clk 0:08:57] } 21... Nb8 { [%clk 0:09:03] } 22. Rc7 { [%clk 0:08:41] } 22... Bd6 { [%clk 0:08:32] } 23. Rc2 { [%clk 0:07:40] } 23... f6 { [%clk 0:07:52] } 24. Kg2 { [%clk 0:07:05] } 24... e5 { [%clk 0:07:41] } 25. h4 { [%clk 0:06:40] } 25... e4 { [%clk 0:07:41] } 26. Nd4 { [%clk 0:06:34] } 26... Kf7 { [%clk 0:07:26] } 27. Nf5 { [%clk 0:06:13] } 27... Nd7 { [%clk 0:06:42] } 28. Nxd6+ { [%clk 0:06:04] } { Black resigns. } 1-0 diff --git a/test/pgn_examples/3.pgn b/test/pgn_examples/3.pgn new file mode 100644 index 0000000..d5f49a8 --- /dev/null +++ b/test/pgn_examples/3.pgn @@ -0,0 +1,19 @@ +[Event "Live Chess"] +[Site "Chess.com"] +[Date "2024.04.15"] +[Round "?"] +[White "Huan_Q"] +[Black "samahe2"] +[Result "1-0"] +[ECO "A21"] +[WhiteElo "1326"] +[BlackElo "1285"] +[TimeControl "300"] +[EndTime "14:20:02 PDT"] +[Termination "Huan_Q won by checkmate"] + +1. c4 e5 2. Nc3 Bc5 3. Nf3 Nf6 4. Nxe5 Bxf2+ 5. Kxf2 d6 6. Nf3 Ng4+ 7. Kg1 c6 8. +d4 a6 9. Bf4 O-O 10. Qd3 Qf6 11. Bd2 Bf5 12. Ne4 Qg6 13. Nh4 Qe6 14. Nxf5 Qxf5 +15. Qf3 Qg6 16. h3 d5 17. hxg4 dxe4 18. Qf4 f5 19. Rh4 Qf6 20. Qf2 f4 21. g5 +Qxg5 22. Rxf4 Rxf4 23. Qxf4 Qe7 24. e3 Nd7 25. Be2 Rf8 26. Qg3 c5 27. d5 Ne5 28. +Bc3 Nd3 29. Bxd3 exd3 30. Rd1 Qe4 31. Qxg7# 1-0 \ No newline at end of file diff --git a/test/pgn_examples/4.pgn b/test/pgn_examples/4.pgn new file mode 100644 index 0000000..3f85810 --- /dev/null +++ b/test/pgn_examples/4.pgn @@ -0,0 +1,121 @@ +[Event "Obert Barad 2024"] +[Site "Terrassa"] +[Date "2024.04.12"] +[Round "1"] +[White "Alejandro Fernandez Cardenete"] +[Black "Joan Amat Iglesis"] +[Result "0-1"] +[WhiteElo "1873"] +[WhiteUrl ""] +[WhiteCountry "ES"] +[WhiteTitle ""] +[BlackElo "1700"] +[TimeControl "90+30"] +[Termination "Black Won"] + +1. d4 c5 2. d5 Nf6 3. c4 e6 4. Nc3 exd5 5. cxd5 d6 6. e4 g6 7. Nf3 Bg7 8. Be2 +O-O 9. O-O Bg4 {ULTIMA JUGADA DE MESTRE, BG4 392 VEGADES 35/31/34\%} 10. Be3 +{Aquí acaba la meva preparació. Be3 no és habitual però no és dolent. Segueixo +amb mainline Re8. STOCKFISH (d50) DONA 0.24 ELS MESTRES JUGUEN BF4 ND2 H3 BG5 +RE1. ELS MESTRES HAN JUGAT BE3 8 VEGADES} 10... Re8 {ULTIMA JUGADA LICHESS >100 +VEGADES 43/3/54\%} 11. Qc2 {No és bona però no veig com explotar-ho. Dubto entre +Na6 i a6. Trio a6 perquè em sembla que Nb5o n té objectius (però Ne5 tampoc !). +STOCKFISH (d40) DONA 0.0 I DEMANA NBD7 (0.08), A6 (0.08), QE7 (0.04) O NA6 +(0.28), TOTES TEMÀTIQUES} 11... a6 12. a4 Nbd7 13. h3 {La meva preparació diu +que l'alfil ha vingut a morir per ralentitzar l'atac de costat de Rei} 13... +Bxf3! {[%c_effect +f3;square;f3;type;GreatFind;path;null;size;100%2525252525252525252525;persistent;true]} +14. Bxf3 {No tinc idea de què fer perquè Ne5 no té objectius ! Sabia per +preparació que que Qc7 és habitual, permet c4 (però no té objectius !) i unir +torres. Descarto Ne5 perquè després de Be2 ve f4 i guanya molta iniciativa. +Considero Qe7 però em sembla que després de Re1 quedarà mal colocada. Considero +Rb8 però em sembla que obrir queenside em perjudica perquè hi té més peces. +STOCKFISH (D=40) VOL QE7 (0.08) C4 (0.11) O RC8 (0.13).} 14... Qc7 {STOCKFISH +(D=35) DONA A5 (0.46) NO ESTAMOS TAN MAL. TRANSPOSEM A LICHESS 40 VEGADES +35/12/53\%} 15. Be2 {Efectivament, volia fer Be2 igualment i així no l'he +ajudat, em guardo c4 per més endavant i preparo les torres, anticipant f3 Nh5 +amb idees a Ng3 i amb Bfosc. STOCKFISH (D=35) DONA C4 (0.0)} 15... Re7?! +{[%c_effect +e7;square;e7;type;Inaccuracy;path;null;size;100%2525252525252525252525;persistent;true] +\"?!\" STOCKFISH (D=35) DONA G4 (0.44). POC HUMÀ. 3 PARTIDES DE GM HAN ARRIBAT +AQUÍ JUGANT F3 (2) RFE1 (1)} 16. f3 {Tinc la iniciativa, calculo Re8 Nh5... +STOCKFISH (D=30) DONA NH5 (0.0) VS ALTRES (0.4-0.5)} 16... Rae8 {STOCKFISH +(D=30) NOMÉS DONA G4 (0.45) VS ALTRES (0.0)} 17. Bf4? {[%c_effect +f4;square;f4;type;Mistake;path;null;size;100%2525252525252525252525;persistent;true] +No me la esperava, em sembla dolenta. Calculo Nh5 Bh2 Bxc3/Bd4+/Be5 i veig que +totes tenen possibilitats. STOCKFISH DONA \" ?\" (D=40) NH5 (-0.21 !) VS ALTRES +(0.3+)} 17... Nh5 18. Bh2 {Intermezzo Bd4+ obre possibilitats futures de Ng3, f4 +és impossible perquè Bxc3 Rxe5. STOCKFISH DONA (D=40) BD4 (-0.21) I F5 (-0.19) +VS ALTRES (0.45+)} 18... Bd4+ (18... f5 19. exf5 (19. g4 Bd4+ 20. Kh1 Nhf6 21. +gxf5 gxf5 {STOCKFISH (D=30) = -0.23}) (19. Qd2 Bd4+ 20. Kh1 f4 21. Bxf4 Nxf4 22. +Qxf4 Nf6 {STOCKFISH (D=30) = -0.52}) 19... Bxc3 {STOCKFISH (D=30) -3.58}) 19. +Kh1 {Penso que Be5 f4 seria un gran error, que farà Be5 Bxe5 Nxe5 Kh2 ? f4 i no +sabré per on continuar, però no veig per on progressar. STOCKFISH (D=30) DONA F5 +(-0.16), BE5 (0.39) VS ALTRES (0.54+)} 19... Be5?! {[%c_effect +e5;square;e5;type;Inaccuracy;persistent;true] STOCKFISH DONA \"?!\" (D=30) = +-0.40 BXE5} (19... f5 20. g4 (20. Bd3 c4 {STOCKFISH (D=20) = -0.38}) (20. exf5 +Bxc3 {STOCKFISH (D=20) = -2.32}) 20... Nhf6 21. gxf5 gxf5 {STOCKFISH (D=25) = +-0.21}) 20. f4 {Blunder ? Guanyo peó però no calculo Bxh5 al final de la +seqüència !! STOCKFISH DONA (D=20) = 0.06} (20. Bxe5 Nxe5 21. Kh2 Nf4 22. Qd2 +Nxe2 23. Nxe2 {STOCKFISH (D=30) = 0.26}) 20... Bxc3! {[%c_effect +c3;square;c3;type;GreatFind;persistent;true] STOCKFISH (D=35) DONA QXC3 (0.0) I +BXC3 (0.0)} 21. Qxc3 Rxe4 22. Bxh5! {[%c_effect +h5;square;h5;type;GreatFind;persistent;true]} 22... gxh5 {STOCKFISH (D=40) DONA +QG3+ (0.0 PERPETU) F5 (0.0) I RF3 (-0.01)} 23. Rf3?! {[%c_effect +f3;square;f3;type;Inaccuracy;persistent;true] L'atac que ve fa por però crec que +trobo la millor defensa. STOCKFISDH DONA \"?!\" (D=35) H4 (-0.04), QB6 (0.0), F6 +(-0.08) I A5 (0.0)} (23. Qg3+ Kh8 24. Qc3+ Kg8 {PERPETU}) (23. f5 f6 24. Qg3+ +Kh8 25. Rae1 Rxe1 26. Rxe1 Rxe1+ 27. Qxe1 Ne5 28. Bxe5 dxe5 {CANVIA TOT I DONA +FINAL DE REINES 0.0}) 23... f6?! {[%c_effect +f6;square;f6;type;Inaccuracy;persistent;true] STOCKFISH \"?!\" (D=30) A5 (-0.06) +B4 (-0.25) VS ALTRES (-0.37+)} 24. g4? {[%c_effect +g4;square;g4;type;Mistake;persistent;true] Considero Kf7 però veig que controlar +g4 és clau STOKFISH \"?\" DONA (D=30) KF7 (-1.68) I H4 (-1.61)} 24... h4 25. g5? +{[%c_effect g5;square;g5;type;Mistake;persistent;true] Considero f5 per tancar +Bfosc però Qd8 ens acosta a cambiar reines i anar a un final ventantjós. Qd8 +permet Qe7 i fer Alekhine Gun a e1 per canviar-ho tot. STOCKFISH DONA (D=30) F5 +(-3.00) QD8 (-2.32)} 25... Qd8?! {[%c_effect +d8;square;d8;type;Inaccuracy;persistent;true]} 26. Rg1?! {[%c_effect +g1;square;g1;type;Inaccuracy;persistent;true] L'atac sembla controlat. STOCKFISH +\"?!\" (D=30) DONA F5 (-2.92) KF7 (-1.2)} 26... Kf7? {[%c_effect +f7;square;f7;type;Mistake;persistent;true]} 27. Qc2 ☒ {Immobilitza la torre però +f5 ho arregla tot i guanya la partida. STOFKFISH DONA (D=30) F5 (-2.80) QE7 +(-1.87) VS ALTRES (-.38+)} (27. Rg4 {-1.36} 27... Re1+ {-1.53} 28. Bg1 {-1.54} +28... Qe7 {-1.50}) 27... f5! {[%c_effect +f5;square;f5;type;GreatFind;persistent;true]} 28. b3?! {[%c_effect +b3;square;b3;type;Inaccuracy;persistent;true] Crec que ho té perdut i aquest +moviment no fa res (només controla c4), necessito convertir canviant reines. +STOCKFISH DONA (D=30) -3.2+ A TOT PER SEMPRE MÉS} 28... Kg6?! {[%c_effect +g6;square;g6;type;Inaccuracy;persistent;true]} 29. Rd3?! {[%c_effect +d3;square;d3;type;Inaccuracy;persistent;true] Ell només calcula Nb6 però jo +tenia calculat Qe7 també} 29... Qe7 30. Qf2 {El seu pla és Qxh3 Qh6. Kh5 Qf3 +repeteix moviments. Vull canviar-ho tot a e1 però considero Re2 Qxh4 Rxh2+ Kxh2 +Qe2+ Kh1 Qxd3 Qh6+ Kf7 Qxh7+ Kf8 Qxd7 i no veig Qxh3# !! ! ME CAGO N +TOOOOOOOOOOT (però quedava calcular un possible g7+ i g8=Q i un possible perpetu +amb la reina). Decideixo canviar a e1 esperant Qxh4 però em quedaria un final +millor perquè tindria la torre infiltrada, millor estructura de peons i cavall +bo contra alfil dolent} 30... Re1 {Espero Qxh4, crec que s'equivoca molt fent +Rxe1} (30... Re2 31. Qxh4 (31. Qf3 Qe4 {AIXÒ IMPEDIA EL M3 PERO PERD IGUAL}) +31... Qe4+ (31... Rxh2+ 32. Kxh2 Qe2+ 33. Kh1 Qxd3 34. Qh6+ Kf7 35. Qxh7+ Kf8 +36. Qxd7 (36. Qh8+ Ke7 37. Re1+ Kd8 {AIXÍ ES PERDIA LA PARTIDA} (37... Ne5 {AIXÒ +MANTENIA L'AVANTATGE})) 36... Qxh3# {AQUESTA SEQÜÈNCIA DE MAT NO LA VEIG}) 32. +Rf3 Qxf3+ 33. Rg2 Qxg2# {VALE DONCS QXH4 NO ERA POSSIBLE PERQUÈ JO TENIA M3}) +31. Rxe1 Qxe1+ 32. Bg1 {Troba una defensa creativa per a intentar guanyar h4, ho +evito} 32... Qe4+ {Espero Rf3 Qxd5 i guanyar avantage a canvi de no simplificar} +33. Qf3 Qxf3+ 34. Rxf3 {Considero Re1/Re2 primer i crec que m'equivoco movent +els peons} 34... b5?! {[%c_effect b5;square;b5;type;Inaccuracy;persistent;true] +NO ÉS UN ERROR TOCKFISH DONA (D=30) AXB5 (-4.10) KG2 (-4.41)} 35. a5?! +{[%c_effect a5;square;a5;type;Inaccuracy;persistent;true] El cavall ja no és +tant bo, però és sol·lucionable, considero Re2 però Re1 em sembla que força Rg2 +i és més previsible} 35... Re1?! {[%c_effect +e1;square;e1;type;Inaccuracy;persistent;true]} 36. Re3?! {[%c_effect +e3;square;e3;type;Inaccuracy;persistent;true] No m'esperava que em forcés ell el +canvi !, calculo que d5 caurá i que guanyaré un 4 vs 2 a costat de reina} 36... +Rxe3 37. Bxe3 {A PARTIR D'AQUÍ STOCKFISH VEU MOLTES JUGADES GUANYADORES (-5)} +37... h6 38. gxh6 Kxh6 39. Kg2 Nf6 40. Kf3 Nxd5 41. Bf2 {No m'agrada gens tirar +Kh5, però considero que cavall + 4 poden amb rei + 2 i mig alfil} 41... Kh5 42. +Be1 {Veig molt fàcil separar els peons, controlar l'alfil i parar la infiltració +del rei} 42... b4 43. Bd2 Ne7 44. Ke2 Nc6 45. Kd3 Nxa5 46. Kc2 Nc6 47. Kd3 {Veig +que cal anar enrrere per controlar la infiltració del rei i poder tirar d5. +STOCKFISH VEU TOT GUANYADOR} 47... Na5 48. Kc2 d5 {Gràcies a que concedeix +perquè no tinc un pla concret per avançar, el meu rei està lligat !} 0-1 diff --git a/test/pgn_examples/5.pgn b/test/pgn_examples/5.pgn new file mode 100644 index 0000000..30d9d58 --- /dev/null +++ b/test/pgn_examples/5.pgn @@ -0,0 +1,120 @@ +[Event "Obert Barad 2024"] +[Site ""] +[Date ""] +[Round "1"] +[White "Alejandro Fernandez Cardenete"] +[Black "Joan Amat Iglesis"] +[Result "0-1"] +[ECO ""] +[WhiteElo "1873"] +[BlackElo "1700"] +[PlyCount ""] +[EventDate ""] +[Source ""] + +1. d4 c5 2. d5 Nf6 3. c4 e6 4. Nc3 exd5 5. cxd5 d6 6. e4 g6 7. Nf3 Bg7 8. Be2 +O-O 9. O-O Bg4 {ULTIMA JUGADA DE MESTRE, BG4 392 VEGADES 35/31/34\%} 10. Be3 +{Aquí acaba la meva preparació. Be3 no és habitual però no és dolent. Segueixo +amb mainline Re8. STOCKFISH (d50) DONA 0.24 ELS MESTRES JUGUEN BF4 ND2 H3 BG5 +RE1. ELS MESTRES HAN JUGAT BE3 8 VEGADES} 10... Re8 {ULTIMA JUGADA LICHESS >100 +VEGADES 43/3/54\%} 11. Qc2 {No és bona però no veig com explotar-ho. Dubto entre +Na6 i a6. Trio a6 perquè em sembla que Nb5o n té objectius (però Ne5 tampoc !). +STOCKFISH (d40) DONA 0.0 I DEMANA NBD7 (0.08), A6 (0.08), QE7 (0.04) O NA6 +(0.28), TOTES TEMÀTIQUES} 11... a6 12. a4 Nbd7 13. h3 {La meva preparació diu +que l'alfil ha vingut a morir per ralentitzar l'atac de costat de Rei} 13... +Bxf3! {[%c_effect +f3;square;f3;type;GreatFind;path;null;size;100%2525252525252525252525;persistent;true]} +14. Bxf3 {No tinc idea de què fer perquè Ne5 no té objectius ! Sabia per +preparació que que Qc7 és habitual, permet c4 (però no té objectius !) i unir +torres. Descarto Ne5 perquè després de Be2 ve f4 i guanya molta iniciativa. +Considero Qe7 però em sembla que després de Re1 quedarà mal colocada. Considero +Rb8 però em sembla que obrir queenside em perjudica perquè hi té més peces. +STOCKFISH (D=40) VOL QE7 (0.08) C4 (0.11) O RC8 (0.13).} 14... Qc7 {STOCKFISH +(D=35) DONA A5 (0.46) NO ESTAMOS TAN MAL. TRANSPOSEM A LICHESS 40 VEGADES +35/12/53\%} 15. Be2 {Efectivament, volia fer Be2 igualment i així no l'he +ajudat, em guardo c4 per més endavant i preparo les torres, anticipant f3 Nh5 +amb idees a Ng3 i amb Bfosc. STOCKFISH (D=35) DONA C4 (0.0)} 15... Re7?! +{[%c_effect +e7;square;e7;type;Inaccuracy;path;null;size;100%2525252525252525252525;persistent;true] +\"?!\" STOCKFISH (D=35) DONA G4 (0.44). POC HUMÀ. 3 PARTIDES DE GM HAN ARRIBAT +AQUÍ JUGANT F3 (2) RFE1 (1)} 16. f3 {Tinc la iniciativa, calculo Re8 Nh5... +STOCKFISH (D=30) DONA NH5 (0.0) VS ALTRES (0.4-0.5)} 16... Rae8 {STOCKFISH +(D=30) NOMÉS DONA G4 (0.45) VS ALTRES (0.0)} 17. Bf4? {[%c_effect +f4;square;f4;type;Mistake;path;null;size;100%2525252525252525252525;persistent;true] +No me la esperava, em sembla dolenta. Calculo Nh5 Bh2 Bxc3/Bd4+/Be5 i veig que +totes tenen possibilitats. STOCKFISH DONA \" ?\" (D=40) NH5 (-0.21 !) VS ALTRES +(0.3+)} 17... Nh5 18. Bh2 {Intermezzo Bd4+ obre possibilitats futures de Ng3, f4 +és impossible perquè Bxc3 Rxe5. STOCKFISH DONA (D=40) BD4 (-0.21) I F5 (-0.19) +VS ALTRES (0.45+)} 18... Bd4+ (18... f5 19. exf5 (19. g4 Bd4+ 20. Kh1 Nhf6 21. +gxf5 gxf5 {STOCKFISH (D=30) = -0.23}) (19. Qd2 Bd4+ 20. Kh1 f4 21. Bxf4 Nxf4 22. +Qxf4 Nf6 {STOCKFISH (D=30) = -0.52}) 19... Bxc3 {STOCKFISH (D=30) -3.58}) 19. +Kh1 {Penso que Be5 f4 seria un gran error, que farà Be5 Bxe5 Nxe5 Kh2 ? f4 i no +sabré per on continuar, però no veig per on progressar. STOCKFISH (D=30) DONA F5 +(-0.16), BE5 (0.39) VS ALTRES (0.54+)} 19... Be5?! {[%c_effect +e5;square;e5;type;Inaccuracy;persistent;true] STOCKFISH DONA \"?!\" (D=30) = +-0.40 BXE5} (19... f5 20. g4 (20. Bd3 c4 {STOCKFISH (D=20) = -0.38}) (20. exf5 +Bxc3 {STOCKFISH (D=20) = -2.32}) 20... Nhf6 21. gxf5 gxf5 {STOCKFISH (D=25) = +-0.21}) 20. f4 {Blunder ? Guanyo peó però no calculo Bxh5 al final de la +seqüència !! STOCKFISH DONA (D=20) = 0.06} (20. Bxe5 Nxe5 21. Kh2 Nf4 22. Qd2 +Nxe2 23. Nxe2 {STOCKFISH (D=30) = 0.26}) 20... Bxc3! {[%c_effect +c3;square;c3;type;GreatFind;persistent;true] STOCKFISH (D=35) DONA QXC3 (0.0) I +BXC3 (0.0)} 21. Qxc3 Rxe4 22. Bxh5! {[%c_effect +h5;square;h5;type;GreatFind;persistent;true]} 22... gxh5 {STOCKFISH (D=40) DONA +QG3+ (0.0 PERPETU) F5 (0.0) I RF3 (-0.01)} 23. Rf3?! {[%c_effect +f3;square;f3;type;Inaccuracy;persistent;true] L'atac que ve fa por però crec que +trobo la millor defensa. STOCKFISDH DONA \"?!\" (D=35) H4 (-0.04), QB6 (0.0), F6 +(-0.08) I A5 (0.0)} (23. Qg3+ Kh8 24. Qc3+ Kg8 {PERPETU}) (23. f5 f6 24. Qg3+ +Kh8 25. Rae1 Rxe1 26. Rxe1 Rxe1+ 27. Qxe1 Ne5 28. Bxe5 dxe5 {CANVIA TOT I DONA +FINAL DE REINES 0.0}) 23... f6?! {[%c_effect +f6;square;f6;type;Inaccuracy;persistent;true] STOCKFISH \"?!\" (D=30) A5 (-0.06) +B4 (-0.25) VS ALTRES (-0.37+)} 24. g4? {[%c_effect +g4;square;g4;type;Mistake;persistent;true] Considero Kf7 però veig que controlar +g4 és clau STOKFISH \"?\" DONA (D=30) KF7 (-1.68) I H4 (-1.61)} 24... h4 25. g5? +{[%c_effect g5;square;g5;type;Mistake;persistent;true] Considero f5 per tancar +Bfosc però Qd8 ens acosta a cambiar reines i anar a un final ventantjós. Qd8 +permet Qe7 i fer Alekhine Gun a e1 per canviar-ho tot. STOCKFISH DONA (D=30) F5 +(-3.00) QD8 (-2.32)} 25... Qd8?! {[%c_effect +d8;square;d8;type;Inaccuracy;persistent;true]} 26. Rg1?! {[%c_effect +g1;square;g1;type;Inaccuracy;persistent;true] L'atac sembla controlat. STOCKFISH +\"?!\" (D=30) DONA F5 (-2.92) KF7 (-1.2)} 26... Kf7? {[%c_effect +f7;square;f7;type;Mistake;persistent;true]} 27. Qc2 ☒ {Immobilitza la torre però +f5 ho arregla tot i guanya la partida. STOFKFISH DONA (D=30) F5 (-2.80) QE7 +(-1.87) VS ALTRES (-.38+)} (27. Rg4 {-1.36} 27... Re1+ {-1.53} 28. Bg1 {-1.54} +28... Qe7 {-1.50}) 27... f5! {[%c_effect +f5;square;f5;type;GreatFind;persistent;true]} 28. b3?! {[%c_effect +b3;square;b3;type;Inaccuracy;persistent;true] Crec que ho té perdut i aquest +moviment no fa res (només controla c4), necessito convertir canviant reines. +STOCKFISH DONA (D=30) -3.2+ A TOT PER SEMPRE MÉS} 28... Kg6?! {[%c_effect +g6;square;g6;type;Inaccuracy;persistent;true]} 29. Rd3?! {[%c_effect +d3;square;d3;type;Inaccuracy;persistent;true] Ell només calcula Nb6 però jo +tenia calculat Qe7 també} 29... Qe7 30. Qf2 {El seu pla és Qxh3 Qh6. Kh5 Qf3 +repeteix moviments. Vull canviar-ho tot a e1 però considero Re2 Qxh4 Rxh2+ Kxh2 +Qe2+ Kh1 Qxd3 Qh6+ Kf7 Qxh7+ Kf8 Qxd7 i no veig Qxh3# !! ! ME CAGO N +TOOOOOOOOOOT (però quedava calcular un possible g7+ i g8=Q i un possible perpetu +amb la reina). Decideixo canviar a e1 esperant Qxh4 però em quedaria un final +millor perquè tindria la torre infiltrada, millor estructura de peons i cavall +bo contra alfil dolent} 30... Re1 {Espero Qxh4, crec que s'equivoca molt fent +Rxe1} (30... Re2 31. Qxh4 (31. Qf3 Qe4 {AIXÒ IMPEDIA EL M3 PERO PERD IGUAL}) +31... Qe4+ (31... Rxh2+ 32. Kxh2 Qe2+ 33. Kh1 Qxd3 34. Qh6+ Kf7 35. Qxh7+ Kf8 +36. Qxd7 (36. Qh8+ Ke7 37. Re1+ Kd8 {AIXÍ ES PERDIA LA PARTIDA} (37... Ne5 {AIXÒ +MANTENIA L'AVANTATGE})) 36... Qxh3# {AQUESTA SEQÜÈNCIA DE MAT NO LA VEIG}) 32. +Rf3 Qxf3+ 33. Rg2 Qxg2# {VALE DONCS QXH4 NO ERA POSSIBLE PERQUÈ JO TENIA M3}) +31. Rxe1 Qxe1+ 32. Bg1 {Troba una defensa creativa per a intentar guanyar h4, ho +evito} 32... Qe4+ {Espero Rf3 Qxd5 i guanyar avantage a canvi de no simplificar} +33. Qf3 Qxf3+ 34. Rxf3 {Considero Re1/Re2 primer i crec que m'equivoco movent +els peons} 34... b5?! {[%c_effect b5;square;b5;type;Inaccuracy;persistent;true] +NO ÉS UN ERROR TOCKFISH DONA (D=30) AXB5 (-4.10) KG2 (-4.41)} 35. a5?! +{[%c_effect a5;square;a5;type;Inaccuracy;persistent;true] El cavall ja no és +tant bo, però és sol·lucionable, considero Re2 però Re1 em sembla que força Rg2 +i és més previsible} 35... Re1?! {[%c_effect +e1;square;e1;type;Inaccuracy;persistent;true]} 36. Re3?! {[%c_effect +e3;square;e3;type;Inaccuracy;persistent;true] No m'esperava que em forcés ell el +canvi !, calculo que d5 caurá i que guanyaré un 4 vs 2 a costat de reina} 36... +Rxe3 37. Bxe3 {A PARTIR D'AQUÍ STOCKFISH VEU MOLTES JUGADES GUANYADORES (-5)} +37... h6 38. gxh6 Kxh6 39. Kg2 Nf6 40. Kf3 Nxd5 41. Bf2 {No m'agrada gens tirar +Kh5, però considero que cavall + 4 poden amb rei + 2 i mig alfil} 41... Kh5 42. +Be1 {Veig molt fàcil separar els peons, controlar l'alfil i parar la infiltració +del rei} 42... b4 43. Bd2 Ne7 44. Ke2 Nc6 45. Kd3 Nxa5 46. Kc2 Nc6 47. Kd3 {Veig +que cal anar enrrere per controlar la infiltració del rei i poder tirar d5. +STOCKFISH VEU TOT GUANYADOR} 47... Na5 48. Kc2 d5 {Gràcies a que concedeix +perquè no tinc un pla concret per avançar, el meu rei està lligat !} 0-1 \ No newline at end of file diff --git a/test/pgn_examples/6.pgn b/test/pgn_examples/6.pgn new file mode 100644 index 0000000..3440dc6 --- /dev/null +++ b/test/pgn_examples/6.pgn @@ -0,0 +1,120 @@ +[Event "Obert Barad 2024"] +[Site "Terrassa"] +[Date ""] +[Round "1"] +[White "Alejandro Fernandez Cardenete"] +[Black "Joan Amat Iglesis"] +[Result "0-1"] +[ECO ""] +[WhiteElo "1873"] +[BlackElo "1700"] +[PlyCount ""] +[EventDate ""] +[Source ""] + +1. d4 c5 2. d5 Nf6 3. c4 e6 4. Nc3 exd5 5. cxd5 d6 6. e4 g6 7. Nf3 Bg7 8. Be2 +O-O 9. O-O Bg4 {ULTIMA JUGADA DE MESTRE, BG4 392 VEGADES 35/31/34\%} 10. Be3 +{Aquí acaba la meva preparació. Be3 no és habitual però no és dolent. Segueixo +amb mainline Re8. STOCKFISH (d50) DONA 0.24 ELS MESTRES JUGUEN BF4 ND2 H3 BG5 +RE1. ELS MESTRES HAN JUGAT BE3 8 VEGADES} 10... Re8 {ULTIMA JUGADA LICHESS >100 +VEGADES 43/3/54\%} 11. Qc2 {No és bona però no veig com explotar-ho. Dubto entre +Na6 i a6. Trio a6 perquè em sembla que Nb5o n té objectius (però Ne5 tampoc !). +STOCKFISH (d40) DONA 0.0 I DEMANA NBD7 (0.08), A6 (0.08), QE7 (0.04) O NA6 +(0.28), TOTES TEMÀTIQUES} 11... a6 12. a4 Nbd7 13. h3 {La meva preparació diu +que l'alfil ha vingut a morir per ralentitzar l'atac de costat de Rei} 13... +Bxf3! {[%c_effect +f3;square;f3;type;GreatFind;path;null;size;100%2525252525252525252525;persistent;true]} +14. Bxf3 {No tinc idea de què fer perquè Ne5 no té objectius ! Sabia per +preparació que que Qc7 és habitual, permet c4 (però no té objectius !) i unir +torres. Descarto Ne5 perquè després de Be2 ve f4 i guanya molta iniciativa. +Considero Qe7 però em sembla que després de Re1 quedarà mal colocada. Considero +Rb8 però em sembla que obrir queenside em perjudica perquè hi té més peces. +STOCKFISH (D=40) VOL QE7 (0.08) C4 (0.11) O RC8 (0.13).} 14... Qc7 {STOCKFISH +(D=35) DONA A5 (0.46) NO ESTAMOS TAN MAL. TRANSPOSEM A LICHESS 40 VEGADES +35/12/53\%} 15. Be2 {Efectivament, volia fer Be2 igualment i així no l'he +ajudat, em guardo c4 per més endavant i preparo les torres, anticipant f3 Nh5 +amb idees a Ng3 i amb Bfosc. STOCKFISH (D=35) DONA C4 (0.0)} 15... Re7?! +{[%c_effect +e7;square;e7;type;Inaccuracy;path;null;size;100%2525252525252525252525;persistent;true] +\"?!\" STOCKFISH (D=35) DONA G4 (0.44). POC HUMÀ. 3 PARTIDES DE GM HAN ARRIBAT +AQUÍ JUGANT F3 (2) RFE1 (1)} 16. f3 {Tinc la iniciativa, calculo Re8 Nh5... +STOCKFISH (D=30) DONA NH5 (0.0) VS ALTRES (0.4-0.5)} 16... Rae8 {STOCKFISH +(D=30) NOMÉS DONA G4 (0.45) VS ALTRES (0.0)} 17. Bf4? {[%c_effect +f4;square;f4;type;Mistake;path;null;size;100%2525252525252525252525;persistent;true] +No me la esperava, em sembla dolenta. Calculo Nh5 Bh2 Bxc3/Bd4+/Be5 i veig que +totes tenen possibilitats. STOCKFISH DONA \" ?\" (D=40) NH5 (-0.21 !) VS ALTRES +(0.3+)} 17... Nh5 18. Bh2 {Intermezzo Bd4+ obre possibilitats futures de Ng3, f4 +és impossible perquè Bxc3 Rxe5. STOCKFISH DONA (D=40) BD4 (-0.21) I F5 (-0.19) +VS ALTRES (0.45+)} 18... Bd4+ (18... f5 19. exf5 (19. g4 Bd4+ 20. Kh1 Nhf6 21. +gxf5 gxf5 {STOCKFISH (D=30) = -0.23}) (19. Qd2 Bd4+ 20. Kh1 f4 21. Bxf4 Nxf4 22. +Qxf4 Nf6 {STOCKFISH (D=30) = -0.52}) 19... Bxc3 {STOCKFISH (D=30) -3.58}) 19. +Kh1 {Penso que Be5 f4 seria un gran error, que farà Be5 Bxe5 Nxe5 Kh2 ? f4 i no +sabré per on continuar, però no veig per on progressar. STOCKFISH (D=30) DONA F5 +(-0.16), BE5 (0.39) VS ALTRES (0.54+)} 19... Be5?! {[%c_effect +e5;square;e5;type;Inaccuracy;persistent;true] STOCKFISH DONA \"?!\" (D=30) = +-0.40 BXE5} (19... f5 20. g4 (20. Bd3 c4 {STOCKFISH (D=20) = -0.38}) (20. exf5 +Bxc3 {STOCKFISH (D=20) = -2.32}) 20... Nhf6 21. gxf5 gxf5 {STOCKFISH (D=25) = +-0.21}) 20. f4 {Blunder ? Guanyo peó però no calculo Bxh5 al final de la +seqüència !! STOCKFISH DONA (D=20) = 0.06} (20. Bxe5 Nxe5 21. Kh2 Nf4 22. Qd2 +Nxe2 23. Nxe2 {STOCKFISH (D=30) = 0.26}) 20... Bxc3! {[%c_effect +c3;square;c3;type;GreatFind;persistent;true] STOCKFISH (D=35) DONA QXC3 (0.0) I +BXC3 (0.0)} 21. Qxc3 Rxe4 22. Bxh5! {[%c_effect +h5;square;h5;type;GreatFind;persistent;true]} 22... gxh5 {STOCKFISH (D=40) DONA +QG3+ (0.0 PERPETU) F5 (0.0) I RF3 (-0.01)} 23. Rf3?! {[%c_effect +f3;square;f3;type;Inaccuracy;persistent;true] L'atac que ve fa por però crec que +trobo la millor defensa. STOCKFISDH DONA \"?!\" (D=35) H4 (-0.04), QB6 (0.0), F6 +(-0.08) I A5 (0.0)} (23. Qg3+ Kh8 24. Qc3+ Kg8 {PERPETU}) (23. f5 f6 24. Qg3+ +Kh8 25. Rae1 Rxe1 26. Rxe1 Rxe1+ 27. Qxe1 Ne5 28. Bxe5 dxe5 {CANVIA TOT I DONA +FINAL DE REINES 0.0}) 23... f6?! {[%c_effect +f6;square;f6;type;Inaccuracy;persistent;true] STOCKFISH \"?!\" (D=30) A5 (-0.06) +B4 (-0.25) VS ALTRES (-0.37+)} 24. g4? {[%c_effect +g4;square;g4;type;Mistake;persistent;true] Considero Kf7 però veig que controlar +g4 és clau STOKFISH \"?\" DONA (D=30) KF7 (-1.68) I H4 (-1.61)} 24... h4 25. g5? +{[%c_effect g5;square;g5;type;Mistake;persistent;true] Considero f5 per tancar +Bfosc però Qd8 ens acosta a cambiar reines i anar a un final ventantjós. Qd8 +permet Qe7 i fer Alekhine Gun a e1 per canviar-ho tot. STOCKFISH DONA (D=30) F5 +(-3.00) QD8 (-2.32)} 25... Qd8?! {[%c_effect +d8;square;d8;type;Inaccuracy;persistent;true]} 26. Rg1?! {[%c_effect +g1;square;g1;type;Inaccuracy;persistent;true] L'atac sembla controlat. STOCKFISH +\"?!\" (D=30) DONA F5 (-2.92) KF7 (-1.2)} 26... Kf7? {[%c_effect +f7;square;f7;type;Mistake;persistent;true]} 27. Qc2 ☒ {Immobilitza la torre però +f5 ho arregla tot i guanya la partida. STOFKFISH DONA (D=30) F5 (-2.80) QE7 +(-1.87) VS ALTRES (-.38+)} (27. Rg4 {-1.36} 27... Re1+ {-1.53} 28. Bg1 {-1.54} +28... Qe7 {-1.50}) 27... f5! {[%c_effect +f5;square;f5;type;GreatFind;persistent;true]} 28. b3?! {[%c_effect +b3;square;b3;type;Inaccuracy;persistent;true] Crec que ho té perdut i aquest +moviment no fa res (només controla c4), necessito convertir canviant reines. +STOCKFISH DONA (D=30) -3.2+ A TOT PER SEMPRE MÉS} 28... Kg6?! {[%c_effect +g6;square;g6;type;Inaccuracy;persistent;true]} 29. Rd3?! {[%c_effect +d3;square;d3;type;Inaccuracy;persistent;true] Ell només calcula Nb6 però jo +tenia calculat Qe7 també} 29... Qe7 30. Qf2 {El seu pla és Qxh3 Qh6. Kh5 Qf3 +repeteix moviments. Vull canviar-ho tot a e1 però considero Re2 Qxh4 Rxh2+ Kxh2 +Qe2+ Kh1 Qxd3 Qh6+ Kf7 Qxh7+ Kf8 Qxd7 i no veig Qxh3# !! ! ME CAGO N +TOOOOOOOOOOT (però quedava calcular un possible g7+ i g8=Q i un possible perpetu +amb la reina). Decideixo canviar a e1 esperant Qxh4 però em quedaria un final +millor perquè tindria la torre infiltrada, millor estructura de peons i cavall +bo contra alfil dolent} 30... Re1 {Espero Qxh4, crec que s'equivoca molt fent +Rxe1} (30... Re2 31. Qxh4 (31. Qf3 Qe4 {AIXÒ IMPEDIA EL M3 PERO PERD IGUAL}) +31... Qe4+ (31... Rxh2+ 32. Kxh2 Qe2+ 33. Kh1 Qxd3 34. Qh6+ Kf7 35. Qxh7+ Kf8 +36. Qxd7 (36. Qh8+ Ke7 37. Re1+ Kd8 {AIXÍ ES PERDIA LA PARTIDA} (37... Ne5 {AIXÒ +MANTENIA L'AVANTATGE})) 36... Qxh3# {AQUESTA SEQÜÈNCIA DE MAT NO LA VEIG}) 32. +Rf3 Qxf3+ 33. Rg2 Qxg2# {VALE DONCS QXH4 NO ERA POSSIBLE PERQUÈ JO TENIA M3}) +31. Rxe1 Qxe1+ 32. Bg1 {Troba una defensa creativa per a intentar guanyar h4, ho +evito} 32... Qe4+ {Espero Rf3 Qxd5 i guanyar avantage a canvi de no simplificar} +33. Qf3 Qxf3+ 34. Rxf3 {Considero Re1/Re2 primer i crec que m'equivoco movent +els peons} 34... b5?! {[%c_effect b5;square;b5;type;Inaccuracy;persistent;true] +NO ÉS UN ERROR TOCKFISH DONA (D=30) AXB5 (-4.10) KG2 (-4.41)} 35. a5?! +{[%c_effect a5;square;a5;type;Inaccuracy;persistent;true] El cavall ja no és +tant bo, però és sol·lucionable, considero Re2 però Re1 em sembla que força Rg2 +i és més previsible} 35... Re1?! {[%c_effect +e1;square;e1;type;Inaccuracy;persistent;true]} 36. Re3?! {[%c_effect +e3;square;e3;type;Inaccuracy;persistent;true] No m'esperava que em forcés ell el +canvi !, calculo que d5 caurá i que guanyaré un 4 vs 2 a costat de reina} 36... +Rxe3 37. Bxe3 {A PARTIR D'AQUÍ STOCKFISH VEU MOLTES JUGADES GUANYADORES (-5)} +37... h6 38. gxh6 Kxh6 39. Kg2 Nf6 40. Kf3 Nxd5 41. Bf2 {No m'agrada gens tirar +Kh5, però considero que cavall + 4 poden amb rei + 2 i mig alfil} 41... Kh5 42. +Be1 {Veig molt fàcil separar els peons, controlar l'alfil i parar la infiltració +del rei} 42... b4 43. Bd2 Ne7 44. Ke2 Nc6 45. Kd3 Nxa5 46. Kc2 Nc6 47. Kd3 {Veig +que cal anar enrrere per controlar la infiltració del rei i poder tirar d5. +STOCKFISH VEU TOT GUANYADOR} 47... Na5 48. Kc2 d5 {Gràcies a que concedeix +perquè no tinc un pla concret per avançar, el meu rei està lligat !} 0-1 \ No newline at end of file diff --git a/test/pgn_examples/7.pgn b/test/pgn_examples/7.pgn new file mode 100644 index 0000000..c9953e4 --- /dev/null +++ b/test/pgn_examples/7.pgn @@ -0,0 +1,112 @@ +[Title "Title Goes Here"] +[Subtitle "Subtitle Goes Here"] +[Date "2024.5.5"] +[Author "Owen Rees"] +[Result "0-1"] + +1. d4 c5 2. d5 Nf6 3. c4 e6 4. Nc3 exd5 5. cxd5 d6 6. e4 g6 7. Nf3 Bg7 8. Be2 +O-O 9. O-O Bg4 {ULTIMA JUGADA DE MESTRE, BG4 392 VEGADES 35/31/34\%} 10. Be3 +{Aquí acaba la meva preparació. Be3 no és habitual però no és dolent. Segueixo +amb mainline Re8. STOCKFISH (d50) DONA 0.24 ELS MESTRES JUGUEN BF4 ND2 H3 BG5 +RE1. ELS MESTRES HAN JUGAT BE3 8 VEGADES} 10... Re8 {ULTIMA JUGADA LICHESS >100 +VEGADES 43/3/54\%} 11. Qc2 {No és bona però no veig com explotar-ho. Dubto entre +Na6 i a6. Trio a6 perquè em sembla que Nb5o n té objectius (però Ne5 tampoc !). +STOCKFISH (d40) DONA 0.0 I DEMANA NBD7 (0.08), A6 (0.08), QE7 (0.04) O NA6 +(0.28), TOTES TEMÀTIQUES} 11... a6 12. a4 Nbd7 13. h3 {La meva preparació diu +que l'alfil ha vingut a morir per ralentitzar l'atac de costat de Rei} 13... +Bxf3! {[%c_effect +f3;square;f3;type;GreatFind;path;null;size;100%2525252525252525252525;persistent;true]} +14. Bxf3 {No tinc idea de què fer perquè Ne5 no té objectius ! Sabia per +preparació que que Qc7 és habitual, permet c4 (però no té objectius !) i unir +torres. Descarto Ne5 perquè després de Be2 ve f4 i guanya molta iniciativa. +Considero Qe7 però em sembla que després de Re1 quedarà mal colocada. Considero +Rb8 però em sembla que obrir queenside em perjudica perquè hi té més peces. +STOCKFISH (D=40) VOL QE7 (0.08) C4 (0.11) O RC8 (0.13).} 14... Qc7 {STOCKFISH +(D=35) DONA A5 (0.46) NO ESTAMOS TAN MAL. TRANSPOSEM A LICHESS 40 VEGADES +35/12/53\%} 15. Be2 {Efectivament, volia fer Be2 igualment i així no l'he +ajudat, em guardo c4 per més endavant i preparo les torres, anticipant f3 Nh5 +amb idees a Ng3 i amb Bfosc. STOCKFISH (D=35) DONA C4 (0.0)} 15... Re7?! +{[%c_effect +e7;square;e7;type;Inaccuracy;path;null;size;100%2525252525252525252525;persistent;true] +\"?!\" STOCKFISH (D=35) DONA G4 (0.44). POC HUMÀ. 3 PARTIDES DE GM HAN ARRIBAT +AQUÍ JUGANT F3 (2) RFE1 (1)} 16. f3 {Tinc la iniciativa, calculo Re8 Nh5... +STOCKFISH (D=30) DONA NH5 (0.0) VS ALTRES (0.4-0.5)} 16... Rae8 {STOCKFISH +(D=30) NOMÉS DONA G4 (0.45) VS ALTRES (0.0)} 17. Bf4? {[%c_effect +f4;square;f4;type;Mistake;path;null;size;100%2525252525252525252525;persistent;true] +No me la esperava, em sembla dolenta. Calculo Nh5 Bh2 Bxc3/Bd4+/Be5 i veig que +totes tenen possibilitats. STOCKFISH DONA \" ?\" (D=40) NH5 (-0.21 !) VS ALTRES +(0.3+)} 17... Nh5 18. Bh2 {Intermezzo Bd4+ obre possibilitats futures de Ng3, f4 +és impossible perquè Bxc3 Rxe5. STOCKFISH DONA (D=40) BD4 (-0.21) I F5 (-0.19) +VS ALTRES (0.45+)} 18... Bd4+ (18... f5 19. exf5 (19. g4 Bd4+ 20. Kh1 Nhf6 21. +gxf5 gxf5 {STOCKFISH (D=30) = -0.23}) (19. Qd2 Bd4+ 20. Kh1 f4 21. Bxf4 Nxf4 22. +Qxf4 Nf6 {STOCKFISH (D=30) = -0.52}) 19... Bxc3 {STOCKFISH (D=30) -3.58}) 19. +Kh1 {Penso que Be5 f4 seria un gran error, que farà Be5 Bxe5 Nxe5 Kh2 ? f4 i no +sabré per on continuar, però no veig per on progressar. STOCKFISH (D=30) DONA F5 +(-0.16), BE5 (0.39) VS ALTRES (0.54+)} 19... Be5?! {[%c_effect +e5;square;e5;type;Inaccuracy;persistent;true] STOCKFISH DONA \"?!\" (D=30) = +-0.40 BXE5} (19... f5 20. g4 (20. Bd3 c4 {STOCKFISH (D=20) = -0.38}) (20. exf5 +Bxc3 {STOCKFISH (D=20) = -2.32}) 20... Nhf6 21. gxf5 gxf5 {STOCKFISH (D=25) = +-0.21}) 20. f4 {Blunder ? Guanyo peó però no calculo Bxh5 al final de la +seqüència !! STOCKFISH DONA (D=20) = 0.06} (20. Bxe5 Nxe5 21. Kh2 Nf4 22. Qd2 +Nxe2 23. Nxe2 {STOCKFISH (D=30) = 0.26}) 20... Bxc3! {[%c_effect +c3;square;c3;type;GreatFind;persistent;true] STOCKFISH (D=35) DONA QXC3 (0.0) I +BXC3 (0.0)} 21. Qxc3 Rxe4 22. Bxh5! {[%c_effect +h5;square;h5;type;GreatFind;persistent;true]} 22... gxh5 {STOCKFISH (D=40) DONA +QG3+ (0.0 PERPETU) F5 (0.0) I RF3 (-0.01)} 23. Rf3?! {[%c_effect +f3;square;f3;type;Inaccuracy;persistent;true] L'atac que ve fa por però crec que +trobo la millor defensa. STOCKFISDH DONA \"?!\" (D=35) H4 (-0.04), QB6 (0.0), F6 +(-0.08) I A5 (0.0)} (23. Qg3+ Kh8 24. Qc3+ Kg8 {PERPETU}) (23. f5 f6 24. Qg3+ +Kh8 25. Rae1 Rxe1 26. Rxe1 Rxe1+ 27. Qxe1 Ne5 28. Bxe5 dxe5 {CANVIA TOT I DONA +FINAL DE REINES 0.0}) 23... f6?! {[%c_effect +f6;square;f6;type;Inaccuracy;persistent;true] STOCKFISH \"?!\" (D=30) A5 (-0.06) +B4 (-0.25) VS ALTRES (-0.37+)} 24. g4? {[%c_effect +g4;square;g4;type;Mistake;persistent;true] Considero Kf7 però veig que controlar +g4 és clau STOKFISH \"?\" DONA (D=30) KF7 (-1.68) I H4 (-1.61)} 24... h4 25. g5? +{[%c_effect g5;square;g5;type;Mistake;persistent;true] Considero f5 per tancar +Bfosc però Qd8 ens acosta a cambiar reines i anar a un final ventantjós. Qd8 +permet Qe7 i fer Alekhine Gun a e1 per canviar-ho tot. STOCKFISH DONA (D=30) F5 +(-3.00) QD8 (-2.32)} 25... Qd8?! {[%c_effect +d8;square;d8;type;Inaccuracy;persistent;true]} 26. Rg1?! {[%c_effect +g1;square;g1;type;Inaccuracy;persistent;true] L'atac sembla controlat. STOCKFISH +\"?!\" (D=30) DONA F5 (-2.92) KF7 (-1.2)} 26... Kf7? {[%c_effect +f7;square;f7;type;Mistake;persistent;true]} 27. Qc2 ☒ {Immobilitza la torre però +f5 ho arregla tot i guanya la partida. STOFKFISH DONA (D=30) F5 (-2.80) QE7 +(-1.87) VS ALTRES (-.38+)} (27. Rg4 {-1.36} 27... Re1+ {-1.53} 28. Bg1 {-1.54} +28... Qe7 {-1.50}) 27... f5! {[%c_effect +f5;square;f5;type;GreatFind;persistent;true]} 28. b3?! {[%c_effect +b3;square;b3;type;Inaccuracy;persistent;true] Crec que ho té perdut i aquest +moviment no fa res (només controla c4), necessito convertir canviant reines. +STOCKFISH DONA (D=30) -3.2+ A TOT PER SEMPRE MÉS} 28... Kg6?! {[%c_effect +g6;square;g6;type;Inaccuracy;persistent;true]} 29. Rd3?! {[%c_effect +d3;square;d3;type;Inaccuracy;persistent;true] Ell només calcula Nb6 però jo +tenia calculat Qe7 també} 29... Qe7 30. Qf2 {El seu pla és Qxh3 Qh6. Kh5 Qf3 +repeteix moviments. Vull canviar-ho tot a e1 però considero Re2 Qxh4 Rxh2+ Kxh2 +Qe2+ Kh1 Qxd3 Qh6+ Kf7 Qxh7+ Kf8 Qxd7 i no veig Qxh3# !! ! ME CAGO N +TOOOOOOOOOOT (però quedava calcular un possible g7+ i g8=Q i un possible perpetu +amb la reina). Decideixo canviar a e1 esperant Qxh4 però em quedaria un final +millor perquè tindria la torre infiltrada, millor estructura de peons i cavall +bo contra alfil dolent} 30... Re1 {Espero Qxh4, crec que s'equivoca molt fent +Rxe1} (30... Re2 31. Qxh4 (31. Qf3 Qe4 {AIXÒ IMPEDIA EL M3 PERO PERD IGUAL}) +31... Qe4+ (31... Rxh2+ 32. Kxh2 Qe2+ 33. Kh1 Qxd3 34. Qh6+ Kf7 35. Qxh7+ Kf8 +36. Qxd7 (36. Qh8+ Ke7 37. Re1+ Kd8 {AIXÍ ES PERDIA LA PARTIDA} (37... Ne5 {AIXÒ +MANTENIA L'AVANTATGE})) 36... Qxh3# {AQUESTA SEQÜÈNCIA DE MAT NO LA VEIG}) 32. +Rf3 Qxf3+ 33. Rg2 Qxg2# {VALE DONCS QXH4 NO ERA POSSIBLE PERQUÈ JO TENIA M3}) +31. Rxe1 Qxe1+ 32. Bg1 {Troba una defensa creativa per a intentar guanyar h4, ho +evito} 32... Qe4+ {Espero Rf3 Qxd5 i guanyar avantage a canvi de no simplificar} +33. Qf3 Qxf3+ 34. Rxf3 {Considero Re1/Re2 primer i crec que m'equivoco movent +els peons} 34... b5?! {[%c_effect b5;square;b5;type;Inaccuracy;persistent;true] +NO ÉS UN ERROR TOCKFISH DONA (D=30) AXB5 (-4.10) KG2 (-4.41)} 35. a5?! +{[%c_effect a5;square;a5;type;Inaccuracy;persistent;true] El cavall ja no és +tant bo, però és sol·lucionable, considero Re2 però Re1 em sembla que força Rg2 +i és més previsible} 35... Re1?! {[%c_effect +e1;square;e1;type;Inaccuracy;persistent;true]} 36. Re3?! {[%c_effect +e3;square;e3;type;Inaccuracy;persistent;true] No m'esperava que em forcés ell el +canvi !, calculo que d5 caurá i que guanyaré un 4 vs 2 a costat de reina} 36... +Rxe3 37. Bxe3 {A PARTIR D'AQUÍ STOCKFISH VEU MOLTES JUGADES GUANYADORES (-5)} +37... h6 38. gxh6 Kxh6 39. Kg2 Nf6 40. Kf3 Nxd5 41. Bf2 {No m'agrada gens tirar +Kh5, però considero que cavall + 4 poden amb rei + 2 i mig alfil} 41... Kh5 42. +Be1 {Veig molt fàcil separar els peons, controlar l'alfil i parar la infiltració +del rei} 42... b4 43. Bd2 Ne7 44. Ke2 Nc6 45. Kd3 Nxa5 46. Kc2 Nc6 47. Kd3 {Veig +que cal anar enrrere per controlar la infiltració del rei i poder tirar d5. +STOCKFISH VEU TOT GUANYADOR} 47... Na5 48. Kc2 d5 {Gràcies a que concedeix +perquè no tinc un pla concret per avançar, el meu rei està lligat !} 0-1 \ No newline at end of file diff --git a/test/pgn_examples/8.pgn b/test/pgn_examples/8.pgn new file mode 100644 index 0000000..8054f57 --- /dev/null +++ b/test/pgn_examples/8.pgn @@ -0,0 +1,109 @@ +[Title "Title Goes Here"] +[Date "2024.5.5"] + +1. d4 c5 2. d5 Nf6 3. c4 e6 4. Nc3 exd5 5. cxd5 d6 6. e4 g6 7. Nf3 Bg7 8. Be2 +O-O 9. O-O Bg4 {ULTIMA JUGADA DE MESTRE, BG4 392 VEGADES 35/31/34\%} 10. Be3 +{Aquí acaba la meva preparació. Be3 no és habitual però no és dolent. Segueixo +amb mainline Re8. STOCKFISH (d50) DONA 0.24 ELS MESTRES JUGUEN BF4 ND2 H3 BG5 +RE1. ELS MESTRES HAN JUGAT BE3 8 VEGADES} 10... Re8 {ULTIMA JUGADA LICHESS >100 +VEGADES 43/3/54\%} 11. Qc2 {No és bona però no veig com explotar-ho. Dubto entre +Na6 i a6. Trio a6 perquè em sembla que Nb5o n té objectius (però Ne5 tampoc !). +STOCKFISH (d40) DONA 0.0 I DEMANA NBD7 (0.08), A6 (0.08), QE7 (0.04) O NA6 +(0.28), TOTES TEMÀTIQUES} 11... a6 12. a4 Nbd7 13. h3 {La meva preparació diu +que l'alfil ha vingut a morir per ralentitzar l'atac de costat de Rei} 13... +Bxf3! {[%c_effect +f3;square;f3;type;GreatFind;path;null;size;100%2525252525252525252525;persistent;true]} +14. Bxf3 {No tinc idea de què fer perquè Ne5 no té objectius ! Sabia per +preparació que que Qc7 és habitual, permet c4 (però no té objectius !) i unir +torres. Descarto Ne5 perquè després de Be2 ve f4 i guanya molta iniciativa. +Considero Qe7 però em sembla que després de Re1 quedarà mal colocada. Considero +Rb8 però em sembla que obrir queenside em perjudica perquè hi té més peces. +STOCKFISH (D=40) VOL QE7 (0.08) C4 (0.11) O RC8 (0.13).} 14... Qc7 {STOCKFISH +(D=35) DONA A5 (0.46) NO ESTAMOS TAN MAL. TRANSPOSEM A LICHESS 40 VEGADES +35/12/53\%} 15. Be2 {Efectivament, volia fer Be2 igualment i així no l'he +ajudat, em guardo c4 per més endavant i preparo les torres, anticipant f3 Nh5 +amb idees a Ng3 i amb Bfosc. STOCKFISH (D=35) DONA C4 (0.0)} 15... Re7?! +{[%c_effect +e7;square;e7;type;Inaccuracy;path;null;size;100%2525252525252525252525;persistent;true] +\"?!\" STOCKFISH (D=35) DONA G4 (0.44). POC HUMÀ. 3 PARTIDES DE GM HAN ARRIBAT +AQUÍ JUGANT F3 (2) RFE1 (1)} 16. f3 {Tinc la iniciativa, calculo Re8 Nh5... +STOCKFISH (D=30) DONA NH5 (0.0) VS ALTRES (0.4-0.5)} 16... Rae8 {STOCKFISH +(D=30) NOMÉS DONA G4 (0.45) VS ALTRES (0.0)} 17. Bf4? {[%c_effect +f4;square;f4;type;Mistake;path;null;size;100%2525252525252525252525;persistent;true] +No me la esperava, em sembla dolenta. Calculo Nh5 Bh2 Bxc3/Bd4+/Be5 i veig que +totes tenen possibilitats. STOCKFISH DONA \" ?\" (D=40) NH5 (-0.21 !) VS ALTRES +(0.3+)} 17... Nh5 18. Bh2 {Intermezzo Bd4+ obre possibilitats futures de Ng3, f4 +és impossible perquè Bxc3 Rxe5. STOCKFISH DONA (D=40) BD4 (-0.21) I F5 (-0.19) +VS ALTRES (0.45+)} 18... Bd4+ (18... f5 19. exf5 (19. g4 Bd4+ 20. Kh1 Nhf6 21. +gxf5 gxf5 {STOCKFISH (D=30) = -0.23}) (19. Qd2 Bd4+ 20. Kh1 f4 21. Bxf4 Nxf4 22. +Qxf4 Nf6 {STOCKFISH (D=30) = -0.52}) 19... Bxc3 {STOCKFISH (D=30) -3.58}) 19. +Kh1 {Penso que Be5 f4 seria un gran error, que farà Be5 Bxe5 Nxe5 Kh2 ? f4 i no +sabré per on continuar, però no veig per on progressar. STOCKFISH (D=30) DONA F5 +(-0.16), BE5 (0.39) VS ALTRES (0.54+)} 19... Be5?! {[%c_effect +e5;square;e5;type;Inaccuracy;persistent;true] STOCKFISH DONA \"?!\" (D=30) = +-0.40 BXE5} (19... f5 20. g4 (20. Bd3 c4 {STOCKFISH (D=20) = -0.38}) (20. exf5 +Bxc3 {STOCKFISH (D=20) = -2.32}) 20... Nhf6 21. gxf5 gxf5 {STOCKFISH (D=25) = +-0.21}) 20. f4 {Blunder ? Guanyo peó però no calculo Bxh5 al final de la +seqüència !! STOCKFISH DONA (D=20) = 0.06} (20. Bxe5 Nxe5 21. Kh2 Nf4 22. Qd2 +Nxe2 23. Nxe2 {STOCKFISH (D=30) = 0.26}) 20... Bxc3! {[%c_effect +c3;square;c3;type;GreatFind;persistent;true] STOCKFISH (D=35) DONA QXC3 (0.0) I +BXC3 (0.0)} 21. Qxc3 Rxe4 22. Bxh5! {[%c_effect +h5;square;h5;type;GreatFind;persistent;true]} 22... gxh5 {STOCKFISH (D=40) DONA +QG3+ (0.0 PERPETU) F5 (0.0) I RF3 (-0.01)} 23. Rf3?! {[%c_effect +f3;square;f3;type;Inaccuracy;persistent;true] L'atac que ve fa por però crec que +trobo la millor defensa. STOCKFISDH DONA \"?!\" (D=35) H4 (-0.04), QB6 (0.0), F6 +(-0.08) I A5 (0.0)} (23. Qg3+ Kh8 24. Qc3+ Kg8 {PERPETU}) (23. f5 f6 24. Qg3+ +Kh8 25. Rae1 Rxe1 26. Rxe1 Rxe1+ 27. Qxe1 Ne5 28. Bxe5 dxe5 {CANVIA TOT I DONA +FINAL DE REINES 0.0}) 23... f6?! {[%c_effect +f6;square;f6;type;Inaccuracy;persistent;true] STOCKFISH \"?!\" (D=30) A5 (-0.06) +B4 (-0.25) VS ALTRES (-0.37+)} 24. g4? {[%c_effect +g4;square;g4;type;Mistake;persistent;true] Considero Kf7 però veig que controlar +g4 és clau STOKFISH \"?\" DONA (D=30) KF7 (-1.68) I H4 (-1.61)} 24... h4 25. g5? +{[%c_effect g5;square;g5;type;Mistake;persistent;true] Considero f5 per tancar +Bfosc però Qd8 ens acosta a cambiar reines i anar a un final ventantjós. Qd8 +permet Qe7 i fer Alekhine Gun a e1 per canviar-ho tot. STOCKFISH DONA (D=30) F5 +(-3.00) QD8 (-2.32)} 25... Qd8?! {[%c_effect +d8;square;d8;type;Inaccuracy;persistent;true]} 26. Rg1?! {[%c_effect +g1;square;g1;type;Inaccuracy;persistent;true] L'atac sembla controlat. STOCKFISH +\"?!\" (D=30) DONA F5 (-2.92) KF7 (-1.2)} 26... Kf7? {[%c_effect +f7;square;f7;type;Mistake;persistent;true]} 27. Qc2 ☒ {Immobilitza la torre però +f5 ho arregla tot i guanya la partida. STOFKFISH DONA (D=30) F5 (-2.80) QE7 +(-1.87) VS ALTRES (-.38+)} (27. Rg4 {-1.36} 27... Re1+ {-1.53} 28. Bg1 {-1.54} +28... Qe7 {-1.50}) 27... f5! {[%c_effect +f5;square;f5;type;GreatFind;persistent;true]} 28. b3?! {[%c_effect +b3;square;b3;type;Inaccuracy;persistent;true] Crec que ho té perdut i aquest +moviment no fa res (només controla c4), necessito convertir canviant reines. +STOCKFISH DONA (D=30) -3.2+ A TOT PER SEMPRE MÉS} 28... Kg6?! {[%c_effect +g6;square;g6;type;Inaccuracy;persistent;true]} 29. Rd3?! {[%c_effect +d3;square;d3;type;Inaccuracy;persistent;true] Ell només calcula Nb6 però jo +tenia calculat Qe7 també} 29... Qe7 30. Qf2 {El seu pla és Qxh3 Qh6. Kh5 Qf3 +repeteix moviments. Vull canviar-ho tot a e1 però considero Re2 Qxh4 Rxh2+ Kxh2 +Qe2+ Kh1 Qxd3 Qh6+ Kf7 Qxh7+ Kf8 Qxd7 i no veig Qxh3# !! ! ME CAGO N +TOOOOOOOOOOT (però quedava calcular un possible g7+ i g8=Q i un possible perpetu +amb la reina). Decideixo canviar a e1 esperant Qxh4 però em quedaria un final +millor perquè tindria la torre infiltrada, millor estructura de peons i cavall +bo contra alfil dolent} 30... Re1 {Espero Qxh4, crec que s'equivoca molt fent +Rxe1} (30... Re2 31. Qxh4 (31. Qf3 Qe4 {AIXÒ IMPEDIA EL M3 PERO PERD IGUAL}) +31... Qe4+ (31... Rxh2+ 32. Kxh2 Qe2+ 33. Kh1 Qxd3 34. Qh6+ Kf7 35. Qxh7+ Kf8 +36. Qxd7 (36. Qh8+ Ke7 37. Re1+ Kd8 {AIXÍ ES PERDIA LA PARTIDA} (37... Ne5 {AIXÒ +MANTENIA L'AVANTATGE})) 36... Qxh3# {AQUESTA SEQÜÈNCIA DE MAT NO LA VEIG}) 32. +Rf3 Qxf3+ 33. Rg2 Qxg2# {VALE DONCS QXH4 NO ERA POSSIBLE PERQUÈ JO TENIA M3}) +31. Rxe1 Qxe1+ 32. Bg1 {Troba una defensa creativa per a intentar guanyar h4, ho +evito} 32... Qe4+ {Espero Rf3 Qxd5 i guanyar avantage a canvi de no simplificar} +33. Qf3 Qxf3+ 34. Rxf3 {Considero Re1/Re2 primer i crec que m'equivoco movent +els peons} 34... b5?! {[%c_effect b5;square;b5;type;Inaccuracy;persistent;true] +NO ÉS UN ERROR TOCKFISH DONA (D=30) AXB5 (-4.10) KG2 (-4.41)} 35. a5?! +{[%c_effect a5;square;a5;type;Inaccuracy;persistent;true] El cavall ja no és +tant bo, però és sol·lucionable, considero Re2 però Re1 em sembla que força Rg2 +i és més previsible} 35... Re1?! {[%c_effect +e1;square;e1;type;Inaccuracy;persistent;true]} 36. Re3?! {[%c_effect +e3;square;e3;type;Inaccuracy;persistent;true] No m'esperava que em forcés ell el +canvi !, calculo que d5 caurá i que guanyaré un 4 vs 2 a costat de reina} 36... +Rxe3 37. Bxe3 {A PARTIR D'AQUÍ STOCKFISH VEU MOLTES JUGADES GUANYADORES (-5)} +37... h6 38. gxh6 Kxh6 39. Kg2 Nf6 40. Kf3 Nxd5 41. Bf2 {No m'agrada gens tirar +Kh5, però considero que cavall + 4 poden amb rei + 2 i mig alfil} 41... Kh5 42. +Be1 {Veig molt fàcil separar els peons, controlar l'alfil i parar la infiltració +del rei} 42... b4 43. Bd2 Ne7 44. Ke2 Nc6 45. Kd3 Nxa5 46. Kc2 Nc6 47. Kd3 {Veig +que cal anar enrrere per controlar la infiltració del rei i poder tirar d5. +STOCKFISH VEU TOT GUANYADOR} 47... Na5 48. Kc2 d5 {Gràcies a que concedeix +perquè no tinc un pla concret per avançar, el meu rei està lligat !} 0-1 \ No newline at end of file diff --git a/test/pgn_examples/9.pgn b/test/pgn_examples/9.pgn new file mode 100644 index 0000000..d30db32 --- /dev/null +++ b/test/pgn_examples/9.pgn @@ -0,0 +1,108 @@ +[Title "Title Goes Here"] + +1. d4 c5 2. d5 Nf6 3. c4 e6 4. Nc3 exd5 5. cxd5 d6 6. e4 g6 7. Nf3 Bg7 8. Be2 +O-O 9. O-O Bg4 {ULTIMA JUGADA DE MESTRE, BG4 392 VEGADES 35/31/34\%} 10. Be3 +{Aquí acaba la meva preparació. Be3 no és habitual però no és dolent. Segueixo +amb mainline Re8. STOCKFISH (d50) DONA 0.24 ELS MESTRES JUGUEN BF4 ND2 H3 BG5 +RE1. ELS MESTRES HAN JUGAT BE3 8 VEGADES} 10... Re8 {ULTIMA JUGADA LICHESS >100 +VEGADES 43/3/54\%} 11. Qc2 {No és bona però no veig com explotar-ho. Dubto entre +Na6 i a6. Trio a6 perquè em sembla que Nb5o n té objectius (però Ne5 tampoc !). +STOCKFISH (d40) DONA 0.0 I DEMANA NBD7 (0.08), A6 (0.08), QE7 (0.04) O NA6 +(0.28), TOTES TEMÀTIQUES} 11... a6 12. a4 Nbd7 13. h3 {La meva preparació diu +que l'alfil ha vingut a morir per ralentitzar l'atac de costat de Rei} 13... +Bxf3! {[%c_effect +f3;square;f3;type;GreatFind;path;null;size;100%2525252525252525252525;persistent;true]} +14. Bxf3 {No tinc idea de què fer perquè Ne5 no té objectius ! Sabia per +preparació que que Qc7 és habitual, permet c4 (però no té objectius !) i unir +torres. Descarto Ne5 perquè després de Be2 ve f4 i guanya molta iniciativa. +Considero Qe7 però em sembla que després de Re1 quedarà mal colocada. Considero +Rb8 però em sembla que obrir queenside em perjudica perquè hi té més peces. +STOCKFISH (D=40) VOL QE7 (0.08) C4 (0.11) O RC8 (0.13).} 14... Qc7 {STOCKFISH +(D=35) DONA A5 (0.46) NO ESTAMOS TAN MAL. TRANSPOSEM A LICHESS 40 VEGADES +35/12/53\%} 15. Be2 {Efectivament, volia fer Be2 igualment i així no l'he +ajudat, em guardo c4 per més endavant i preparo les torres, anticipant f3 Nh5 +amb idees a Ng3 i amb Bfosc. STOCKFISH (D=35) DONA C4 (0.0)} 15... Re7?! +{[%c_effect +e7;square;e7;type;Inaccuracy;path;null;size;100%2525252525252525252525;persistent;true] +\"?!\" STOCKFISH (D=35) DONA G4 (0.44). POC HUMÀ. 3 PARTIDES DE GM HAN ARRIBAT +AQUÍ JUGANT F3 (2) RFE1 (1)} 16. f3 {Tinc la iniciativa, calculo Re8 Nh5... +STOCKFISH (D=30) DONA NH5 (0.0) VS ALTRES (0.4-0.5)} 16... Rae8 {STOCKFISH +(D=30) NOMÉS DONA G4 (0.45) VS ALTRES (0.0)} 17. Bf4? {[%c_effect +f4;square;f4;type;Mistake;path;null;size;100%2525252525252525252525;persistent;true] +No me la esperava, em sembla dolenta. Calculo Nh5 Bh2 Bxc3/Bd4+/Be5 i veig que +totes tenen possibilitats. STOCKFISH DONA \" ?\" (D=40) NH5 (-0.21 !) VS ALTRES +(0.3+)} 17... Nh5 18. Bh2 {Intermezzo Bd4+ obre possibilitats futures de Ng3, f4 +és impossible perquè Bxc3 Rxe5. STOCKFISH DONA (D=40) BD4 (-0.21) I F5 (-0.19) +VS ALTRES (0.45+)} 18... Bd4+ (18... f5 19. exf5 (19. g4 Bd4+ 20. Kh1 Nhf6 21. +gxf5 gxf5 {STOCKFISH (D=30) = -0.23}) (19. Qd2 Bd4+ 20. Kh1 f4 21. Bxf4 Nxf4 22. +Qxf4 Nf6 {STOCKFISH (D=30) = -0.52}) 19... Bxc3 {STOCKFISH (D=30) -3.58}) 19. +Kh1 {Penso que Be5 f4 seria un gran error, que farà Be5 Bxe5 Nxe5 Kh2 ? f4 i no +sabré per on continuar, però no veig per on progressar. STOCKFISH (D=30) DONA F5 +(-0.16), BE5 (0.39) VS ALTRES (0.54+)} 19... Be5?! {[%c_effect +e5;square;e5;type;Inaccuracy;persistent;true] STOCKFISH DONA \"?!\" (D=30) = +-0.40 BXE5} (19... f5 20. g4 (20. Bd3 c4 {STOCKFISH (D=20) = -0.38}) (20. exf5 +Bxc3 {STOCKFISH (D=20) = -2.32}) 20... Nhf6 21. gxf5 gxf5 {STOCKFISH (D=25) = +-0.21}) 20. f4 {Blunder ? Guanyo peó però no calculo Bxh5 al final de la +seqüència !! STOCKFISH DONA (D=20) = 0.06} (20. Bxe5 Nxe5 21. Kh2 Nf4 22. Qd2 +Nxe2 23. Nxe2 {STOCKFISH (D=30) = 0.26}) 20... Bxc3! {[%c_effect +c3;square;c3;type;GreatFind;persistent;true] STOCKFISH (D=35) DONA QXC3 (0.0) I +BXC3 (0.0)} 21. Qxc3 Rxe4 22. Bxh5! {[%c_effect +h5;square;h5;type;GreatFind;persistent;true]} 22... gxh5 {STOCKFISH (D=40) DONA +QG3+ (0.0 PERPETU) F5 (0.0) I RF3 (-0.01)} 23. Rf3?! {[%c_effect +f3;square;f3;type;Inaccuracy;persistent;true] L'atac que ve fa por però crec que +trobo la millor defensa. STOCKFISDH DONA \"?!\" (D=35) H4 (-0.04), QB6 (0.0), F6 +(-0.08) I A5 (0.0)} (23. Qg3+ Kh8 24. Qc3+ Kg8 {PERPETU}) (23. f5 f6 24. Qg3+ +Kh8 25. Rae1 Rxe1 26. Rxe1 Rxe1+ 27. Qxe1 Ne5 28. Bxe5 dxe5 {CANVIA TOT I DONA +FINAL DE REINES 0.0}) 23... f6?! {[%c_effect +f6;square;f6;type;Inaccuracy;persistent;true] STOCKFISH \"?!\" (D=30) A5 (-0.06) +B4 (-0.25) VS ALTRES (-0.37+)} 24. g4? {[%c_effect +g4;square;g4;type;Mistake;persistent;true] Considero Kf7 però veig que controlar +g4 és clau STOKFISH \"?\" DONA (D=30) KF7 (-1.68) I H4 (-1.61)} 24... h4 25. g5? +{[%c_effect g5;square;g5;type;Mistake;persistent;true] Considero f5 per tancar +Bfosc però Qd8 ens acosta a cambiar reines i anar a un final ventantjós. Qd8 +permet Qe7 i fer Alekhine Gun a e1 per canviar-ho tot. STOCKFISH DONA (D=30) F5 +(-3.00) QD8 (-2.32)} 25... Qd8?! {[%c_effect +d8;square;d8;type;Inaccuracy;persistent;true]} 26. Rg1?! {[%c_effect +g1;square;g1;type;Inaccuracy;persistent;true] L'atac sembla controlat. STOCKFISH +\"?!\" (D=30) DONA F5 (-2.92) KF7 (-1.2)} 26... Kf7? {[%c_effect +f7;square;f7;type;Mistake;persistent;true]} 27. Qc2 ☒ {Immobilitza la torre però +f5 ho arregla tot i guanya la partida. STOFKFISH DONA (D=30) F5 (-2.80) QE7 +(-1.87) VS ALTRES (-.38+)} (27. Rg4 {-1.36} 27... Re1+ {-1.53} 28. Bg1 {-1.54} +28... Qe7 {-1.50}) 27... f5! {[%c_effect +f5;square;f5;type;GreatFind;persistent;true]} 28. b3?! {[%c_effect +b3;square;b3;type;Inaccuracy;persistent;true] Crec que ho té perdut i aquest +moviment no fa res (només controla c4), necessito convertir canviant reines. +STOCKFISH DONA (D=30) -3.2+ A TOT PER SEMPRE MÉS} 28... Kg6?! {[%c_effect +g6;square;g6;type;Inaccuracy;persistent;true]} 29. Rd3?! {[%c_effect +d3;square;d3;type;Inaccuracy;persistent;true] Ell només calcula Nb6 però jo +tenia calculat Qe7 també} 29... Qe7 30. Qf2 {El seu pla és Qxh3 Qh6. Kh5 Qf3 +repeteix moviments. Vull canviar-ho tot a e1 però considero Re2 Qxh4 Rxh2+ Kxh2 +Qe2+ Kh1 Qxd3 Qh6+ Kf7 Qxh7+ Kf8 Qxd7 i no veig Qxh3# !! ! ME CAGO N +TOOOOOOOOOOT (però quedava calcular un possible g7+ i g8=Q i un possible perpetu +amb la reina). Decideixo canviar a e1 esperant Qxh4 però em quedaria un final +millor perquè tindria la torre infiltrada, millor estructura de peons i cavall +bo contra alfil dolent} 30... Re1 {Espero Qxh4, crec que s'equivoca molt fent +Rxe1} (30... Re2 31. Qxh4 (31. Qf3 Qe4 {AIXÒ IMPEDIA EL M3 PERO PERD IGUAL}) +31... Qe4+ (31... Rxh2+ 32. Kxh2 Qe2+ 33. Kh1 Qxd3 34. Qh6+ Kf7 35. Qxh7+ Kf8 +36. Qxd7 (36. Qh8+ Ke7 37. Re1+ Kd8 {AIXÍ ES PERDIA LA PARTIDA} (37... Ne5 {AIXÒ +MANTENIA L'AVANTATGE})) 36... Qxh3# {AQUESTA SEQÜÈNCIA DE MAT NO LA VEIG}) 32. +Rf3 Qxf3+ 33. Rg2 Qxg2# {VALE DONCS QXH4 NO ERA POSSIBLE PERQUÈ JO TENIA M3}) +31. Rxe1 Qxe1+ 32. Bg1 {Troba una defensa creativa per a intentar guanyar h4, ho +evito} 32... Qe4+ {Espero Rf3 Qxd5 i guanyar avantage a canvi de no simplificar} +33. Qf3 Qxf3+ 34. Rxf3 {Considero Re1/Re2 primer i crec que m'equivoco movent +els peons} 34... b5?! {[%c_effect b5;square;b5;type;Inaccuracy;persistent;true] +NO ÉS UN ERROR TOCKFISH DONA (D=30) AXB5 (-4.10) KG2 (-4.41)} 35. a5?! +{[%c_effect a5;square;a5;type;Inaccuracy;persistent;true] El cavall ja no és +tant bo, però és sol·lucionable, considero Re2 però Re1 em sembla que força Rg2 +i és més previsible} 35... Re1?! {[%c_effect +e1;square;e1;type;Inaccuracy;persistent;true]} 36. Re3?! {[%c_effect +e3;square;e3;type;Inaccuracy;persistent;true] No m'esperava que em forcés ell el +canvi !, calculo que d5 caurá i que guanyaré un 4 vs 2 a costat de reina} 36... +Rxe3 37. Bxe3 {A PARTIR D'AQUÍ STOCKFISH VEU MOLTES JUGADES GUANYADORES (-5)} +37... h6 38. gxh6 Kxh6 39. Kg2 Nf6 40. Kf3 Nxd5 41. Bf2 {No m'agrada gens tirar +Kh5, però considero que cavall + 4 poden amb rei + 2 i mig alfil} 41... Kh5 42. +Be1 {Veig molt fàcil separar els peons, controlar l'alfil i parar la infiltració +del rei} 42... b4 43. Bd2 Ne7 44. Ke2 Nc6 45. Kd3 Nxa5 46. Kc2 Nc6 47. Kd3 {Veig +que cal anar enrrere per controlar la infiltració del rei i poder tirar d5. +STOCKFISH VEU TOT GUANYADOR} 47... Na5 48. Kc2 d5 {Gràcies a que concedeix +perquè no tinc un pla concret per avançar, el meu rei està lligat !} 0-1 \ No newline at end of file diff --git a/test/test_latex.ml b/test/test_latex.ml new file mode 100644 index 0000000..9ab2e1d --- /dev/null +++ b/test/test_latex.ml @@ -0,0 +1,182 @@ +open OUnit2 +open Pgn_logic +open Ast +open Helpers + +let ae expected actual = assert_equal ~printer:(fun x -> x) expected actual + +let test_number_to_latex_direct _ctxt = + let item = Number "1." in + let expected = "\\textbf{1.}" in + let actual = Latex.item_to_tex true 0 item in + ae expected actual + +let test_move_to_latex_direct _ctxt = + let item = Move "e4" in + let expected = "\\textbf{e4}" in + let actual = Latex.item_to_tex true 0 item in + ae expected actual + +let test_comment_to_latex_direct _ctxt = + let comment = "some comment here" in + let item = Comment comment in + let expected = "\\newline " ^ comment ^ "\\par" in + let actual = Latex.item_to_tex true 0 item in + ae expected actual + +let test_basic_pgn _ctxt = + let pgn = "1. e4 e5" in + let parsed_game = Parsing.parse_pgn pgn in + + match parsed_game.content with + | items -> + let expected = "\\textbf{1.} \\textbf{e4} \\textbf{e5}" in + let actual = Latex.render_game true items in + ae expected actual + +let test_basic_longer_pgn _ctxt = + let pgn = "1. e4 e5 2. Nf3 Nf6 3. Bb5" in + let parsed_game = Parsing.parse_pgn pgn in + + match parsed_game.content with + | items -> + let expected = + "\\textbf{1.} \\textbf{e4} \\textbf{e5} \\textbf{2.} \\textbf{Nf3} \ + \\textbf{Nf6} \\textbf{3.} \\textbf{Bb5}" + in + let actual = Latex.render_game true items in + ae expected actual + +let test_variation_pgn _ctxt = + let pgn = "1. e4 (1. d4 d5) 1... e5" in + let parsed_game = Parsing.parse_pgn pgn in + let expected = + "\\textbf{1.} \\textbf{e4} ( 1. d4 d5 ) \\textbf{1...} \\textbf{e5}" + in + let actual = Latex.render_game true parsed_game.content in + ae expected actual + +let test_black_move_after_comment _ctxt = + let pgn = "1. e4 {some comment} e5" in + let parsed_game = Parsing.parse_pgn pgn in + let expected = + "\\textbf{1.} \\textbf{e4} \\newline some comment\\par \ + \\textbf{\\ldots{}e5}" + in + let actual = Latex.render_game true parsed_game.content in + ae expected actual + +let test_diagram_placement _ctxt = + let pgn = "1.e4 e5 2.Nf3 Nc6 3.Bb5 a3" in + let parsed_game = Parsing.parse_pgn pgn in + let rendered_game = + Latex.render_game true ~diagram_data:Helpers.Latex_helper.diagram_data + parsed_game.content + in + let expected = + {|\textbf{1.} \textbf{e4} \textbf{e5} \textbf{2.} \textbf{Nf3} \textbf{Nc6} \textbf{3.} \textbf{Bb5} +\par\nobreak\medskip\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip + \textbf{\ldots{}}\textbf{a3} +\par\nobreak\medskip\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 3, vmargin=false]\par\medskip|} + in + ae expected rendered_game + +let test_player_elo = + [ + ( "White ELO" >:: fun _ -> + ae "(1432)" (Latex_helpers.build_elo_string "1432") ); + ( "Black ELO" >:: fun _ -> + ae "(1233)" (Latex_helpers.build_elo_string "1233") ); + ("Empty ELO" >:: fun _ -> ae "" (Latex_helpers.build_elo_string "")); + ] + +let test_date_site = + [ + ( "Date and Site" >:: fun _ -> + ae "\\date{1991.01.01, Kherson}" + (Latex_helpers.build_date_site_string "1991.01.01" "Kherson") ); + ( "Date and no site" >:: fun _ -> + ae "\\date{1991.01.01}" + (Latex_helpers.build_date_site_string "1991.01.01" "") ); + ( "No date but site" >:: fun _ -> + ae "\\date{Kherson}" (Latex_helpers.build_date_site_string "" "Kherson") + ); + ( "No date and no site" >:: fun _ -> + ae "\\date{}" (Latex_helpers.build_date_site_string "" "") ); + ] + +let test_title = + [ + ( "Event only" >:: fun _ -> + ae "\\title{some event}" + (Latex_helpers.build_title_string "some event" "" "") ); + ( "Title and subtitle" >:: fun _ -> + ae "\\title{Some title}\\\\[2ex]\\large{Some subtitle}" + (Latex_helpers.build_title_string "irrelevant" "Some title" + "Some subtitle") ); + ] + +let test_author = + [ + ( "Author only" >:: fun _ -> + ae "\\author{Some author}" + (Latex_helpers.build_author_string "Some author" "" "" "" "") ); + ( "Nothing" >:: fun _ -> + ae "\\author{}" (Latex_helpers.build_author_string "" "" "" "" "") ); + ] + +let test_diagram _ctxt = + match Latex_helpers.get_diagram 5 Helpers.Latex_helper.diagram_data with + | Some actual -> + let expected = + "\n\\par\\nobreak\\medskip\\chessboard[setfen=" + ^ "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3" + ^ ", vmargin=false]\\par\\medskip\n" + in + ae expected actual + | None -> assert_failure "Diagram for ply 5 was not found" + +let test_diagram_clock _ctxt = + match + Latex_helpers.get_diagram 5 Helpers.Latex_helper.diagram_data ~clock:true + ~white_time:"1:00" ~black_time:"0:21" + with + | Some actual -> + let expected = + {|\par\medskip\noindent\begin{minipage}{\linewidth}0:21\par\nopagebreak\smallskip +\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\nopagebreak\vspace{1em} +1:00\end{minipage}\par\medskip|} + in + ae expected actual + | None -> assert_failure "Diagram for ply 5 was not found" + +let test_no_diagram _ctxt = + let result = Latex_helpers.get_diagram 99 Helpers.Latex_helper.diagram_data in + + match result with + | None -> () + | Some actual -> + assert_failure + ("Expected None for ply 99, but got a diagram string instead: " ^ actual) + +let suite = + "latex tests" + >::: [ + "number_to_latex_direct" >:: test_number_to_latex_direct; + "move_to_latex_direct" >:: test_move_to_latex_direct; + "comment_to_latex_direct" >:: test_comment_to_latex_direct; + "basic_pgn" >:: test_basic_pgn; + "basic_longer_pgn" >:: test_basic_longer_pgn; + "variation_pgn" >:: test_variation_pgn; + "black_move_after_comment" >:: test_black_move_after_comment; + "diagram_placement" >:: test_diagram_placement; + "elo tests" >::: test_player_elo; + "date & site tests" >::: test_date_site; + "title tests" >::: test_title; + "author tests" >::: test_author; + "diagram_test" >:: test_diagram; + "diagram_clock" >:: test_diagram_clock; + "no_diagram" >:: test_no_diagram; + ] + +let () = run_test_tt_main suite diff --git a/test/test_lexer.ml b/test/test_lexer.ml new file mode 100644 index 0000000..d1ec46c --- /dev/null +++ b/test/test_lexer.ml @@ -0,0 +1,191 @@ +open OUnit2 +open Pgn_logic +open Parser + +let lex_all ?(is_header = false) s = + let lexbuf = Sedlexing.Utf8.from_string s in + let rec loop acc = + let tok = + if is_header then Lexer.tokenize_header lexbuf + else Lexer.tokenize_game lexbuf + in + match tok with EOF -> List.rev (EOF :: acc) | t -> loop (t :: acc) + in + loop [] + +let string_of_token = function + | TAG_OPEN -> "TAG_OPEN" + | TAG_CLOSE -> "TAG_CLOSE" + | LPAREN -> "LPAREN" + | RPAREN -> "RPAREN" + | HEADER s -> "HEADER(" ^ s ^ ")" + | STRING s -> "STRING(" ^ s ^ ")" + | MOVE s -> "MOVE(" ^ s ^ ")" + | NUMBER s -> "NUMBER(" ^ s ^ ")" + | COMMENT s -> "COMMENT(" ^ s ^ ")" + | NAG s -> "NAG(" ^ s ^ ")" + | RESULT s -> "RESULT(" ^ s ^ ")" + | CLOCK s -> "CLOCK(" ^ s ^ ")" + | EOF -> "EOF" + +let assert_tokens ~ctxt ~input ~is_header ~expected = + let got = lex_all ~is_header input in + let pp toks = + "[" ^ String.concat "; " (List.map string_of_token toks) ^ "]" + in + assert_equal ~ctxt ~printer:pp expected got + +let test_header_simple _ctxt = + let input = {|Event "Casual Game"]|} in + let expected = [ HEADER "Event"; STRING "Casual Game"; TAG_CLOSE; EOF ] in + assert_tokens ~ctxt:_ctxt ~input ~is_header:true ~expected + +let test_header_escaped_string _ctxt = + let input = {|Site "My \"Quoted\" Site"]|} in + let expected = + [ HEADER "Site"; STRING {|My "Quoted" Site|}; TAG_CLOSE; EOF ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:true ~expected + +let test_numbers_and_moves _ctxt = + let input = "1. e4 e5 2... Nf3 Nc6" in + let expected = + [ + NUMBER "1."; + MOVE "e4"; + MOVE "e5"; + NUMBER "2..."; + MOVE "Nf3"; + MOVE "Nc6"; + EOF; + ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_castling_and_results _ctxt = + let input = "1. O-O O-O-O 1-0" in + let expected = [ NUMBER "1."; MOVE "O-O"; MOVE "O-O-O"; RESULT "1-0"; EOF ] in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_nags _ctxt = + let input = "1. e4$1 $23 Nf3 *" in + let expected = + [ NUMBER "1."; MOVE "e4"; NAG "$1"; NAG "$23"; MOVE "Nf3"; RESULT "*"; EOF ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_parentheses_and_comments _ctxt = + let input = "1. e4 (e5) {a comment} 1/2-1/2" in + let expected = + [ + NUMBER "1."; + MOVE "e4"; + LPAREN; + MOVE "e5"; + RPAREN; + COMMENT "a comment"; + RESULT "1/2-1/2"; + EOF; + ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_comment_multiline _ctxt = + let input = "1. e4 {multi\nline\ncomment} *" in + let expected = + [ NUMBER "1."; MOVE "e4"; COMMENT "multi\nline\ncomment"; RESULT "*"; EOF ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_move_syntax_variants _ctxt = + let input = "1. exd5 e8=Q+ Kxd5 a1=R#" in + let expected = + [ NUMBER "1."; MOVE "exd5"; MOVE "e8=Q+"; MOVE "Kxd5"; MOVE "a1=R#"; EOF ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_king_side_zero_letter_o _ctxt = + let input = "1. O-O *" in + let expected = [ NUMBER "1."; MOVE "O-O"; RESULT "*"; EOF ] in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_unterminated_comment_raises _ctxt = + let input = "1. e4 {oops" in + let lexbuf = Sedlexing.Utf8.from_string input in + let exn_opt = + try + ignore (Lexer.tokenize_game lexbuf); + ignore (Lexer.tokenize_game lexbuf); + ignore (Lexer.tokenize_game lexbuf); + (* this should try to read comment *) + None + with exn -> Some exn + in + assert_bool "Expected exception on unterminated comment" + (Option.is_some exn_opt) + +let test_clock_times _ctxt = + let input = + "1. d4 { [%clk 0:10:00] } 1... Nf6 { [%clk 0:10:00] } 2. Nf3 { [%clk \ + 0:10:00] } 2... d5 { [%clk 0:10:01] } 3. e3 { [%clk 0:10:01] } 3... e6 { \ + [%clk 0:10:02] } { D05 Queen's Pawn Game: Colle System }" + in + let expected = + [ + NUMBER "1."; + MOVE "d4"; + CLOCK "0:10:00"; + NUMBER "1..."; + MOVE "Nf6"; + CLOCK "0:10:00"; + NUMBER "2."; + MOVE "Nf3"; + CLOCK "0:10:00"; + NUMBER "2..."; + MOVE "d5"; + CLOCK "0:10:01"; + NUMBER "3."; + MOVE "e3"; + CLOCK "0:10:01"; + NUMBER "3..."; + MOVE "e6"; + CLOCK "0:10:02"; + COMMENT "D05 Queen's Pawn Game: Colle System"; + EOF; + ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_clock_inline _ctxt = + let input = "1. d4 [%clk 0:10:00] Nf3 *" in + let expected = + [ NUMBER "1."; MOVE "d4"; CLOCK "0:10:00"; MOVE "Nf3"; RESULT "*"; EOF ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let test_clock_in_comment _ctxt = + let input = "1. d4 {[%clk 0:10:00]} Nf3 *" in + let expected = + [ NUMBER "1."; MOVE "d4"; CLOCK "0:10:00"; MOVE "Nf3"; RESULT "*"; EOF ] + in + assert_tokens ~ctxt:_ctxt ~input ~is_header:false ~expected + +let suite = + "lexer tests" + >::: [ + "header_simple" >:: test_header_simple; + "header_escaped_string" >:: test_header_escaped_string; + "numbers_and_moves" >:: test_numbers_and_moves; + "castling_and_results" >:: test_castling_and_results; + "nags" >:: test_nags; + "parentheses_and_comments" >:: test_parentheses_and_comments; + "comment_multiline" >:: test_comment_multiline; + "move_syntax_variants" >:: test_move_syntax_variants; + "king_side_zero_letter_o" >:: test_king_side_zero_letter_o; + "unterminated_comment_raises" >:: test_unterminated_comment_raises; + "clock_times" >:: test_clock_times; + "clock_inline" >:: test_clock_inline; + "clock_in_comment" >:: test_clock_in_comment; + ] + +let () = run_test_tt_main suite diff --git a/test/test_nag.ml b/test/test_nag.ml new file mode 100644 index 0000000..327d8a0 --- /dev/null +++ b/test/test_nag.ml @@ -0,0 +1,30 @@ +open OUnit2 +open Pgn_logic +open Ast +open Nag + +let ae expected actual = assert_equal ~printer:(fun x -> x) expected actual + +let test_direct_nags = + [ + ("$1" >:: fun _ -> ae "!" (nag_to_tex "$1")); + ("$2" >:: fun _ -> ae "?" (nag_to_tex "$2")); + ("$3" >:: fun _ -> ae "!!" (nag_to_tex "$3")); + ("$4" >:: fun _ -> ae "??" (nag_to_tex "$4")); + ("$5" >:: fun _ -> ae "!?" (nag_to_tex "$5")); + ("$6" >:: fun _ -> ae "?!" (nag_to_tex "$6")); + ("$10" >:: fun _ -> ae "=" (nag_to_tex "$10")); + ("$13" >:: fun _ -> ae "\\infty" (nag_to_tex "$13")); + ("$14" >:: fun _ -> ae "\\wbetter" (nag_to_tex "$14")); + ("$15" >:: fun _ -> ae "\\bbetter" (nag_to_tex "$15")); + ("$16" >:: fun _ -> ae "\\ensuremath{\\pm}" (nag_to_tex "$16")); + ("$17" >:: fun _ -> ae "\\ensuremath{\\mp}" (nag_to_tex "$17")); + ("$22" >:: fun _ -> ae "\\zugzwang" (nag_to_tex "$22")); + ("$32" >:: fun _ -> ae "\\development" (nag_to_tex "$32")); + ("$36" >:: fun _ -> ae "\\initiative" (nag_to_tex "$36")); + ("$40" >:: fun _ -> ae "\\attack" (nag_to_tex "$40")); + ("$44" >:: fun _ -> ae "\\compensation" (nag_to_tex "$44")); + ("$132" >:: fun _ -> ae "\\counterplay" (nag_to_tex "$132")); + ] + +let suite = "nag test" >::: [ "direct nags" >::: test_direct_nags ] diff --git a/test/test_pgn2tex.ml b/test/test_pgn2tex.ml new file mode 100644 index 0000000..2b0046f --- /dev/null +++ b/test/test_pgn2tex.ml @@ -0,0 +1,123 @@ +open OUnit2 +open Pgn_logic +open Ast + +let ae expected actual = assert_equal ~printer:(fun x -> x) expected actual + +let diagram_data = + MoveMap.empty + |> MoveMap.add 5 + "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3" + |> MoveMap.add 6 + "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 3" + |> MoveMap.add 10 + "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 3 5" + |> MoveMap.add 15 + "r1bqkb1r/pp1ppppp/2n5/2p5/4P3/2N2N2/PPPP1PPP/R1BQKB1R b KQkq - 5 7" + +let test_basic_pgn_to_tex _ctxt = + let filename = "pgn_examples/1.pgn" in + let ic = In_channel.open_text filename in + let input_string = In_channel.input_all ic in + let actual = Pgn2tex.to_tex input_string ~diagram_data ~clock:false in + let expected = + {|\title{URS-chJ} +\author{Ibragimov, Ildar (2455)\\Kramnik, Vladimir (2480)} +\date{1991.??.??, Kherson}\maketitle +\newchessgame +\textbf{1.} \textbf{d4} \newline A88: Dutch Defence: Leningrad System: 5 Nf3 0-0 6 0-0 d6 7 Nc3 c6\par \textbf{\ldots{}d6} \textbf{2.} \textbf{c4} \textbf{f5} \textbf{3.} \textbf{Nf3} +\par\nobreak\medskip\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip + \textbf{\ldots{}}\textbf{Nf6} +\par\nobreak\medskip\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 3, vmargin=false]\par\medskip + \textbf{4.} \textbf{g3} \textbf{g6} \textbf{5.} \textbf{Bg2} \textbf{Bg7} +\par\nobreak\medskip\chessboard[setfen=r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 3 5, vmargin=false]\par\medskip + \textbf{6.} \textbf{O-O} \textbf{O-O} \textbf{7.} \textbf{Nc3} \textbf{Qe8} \textbf{8.} \textbf{b3} +\par\nobreak\medskip\chessboard[setfen=r1bqkb1r/pp1ppppp/2n5/2p5/4P3/2N2N2/PPPP1PPP/R1BQKB1R b KQkq - 5 7, vmargin=false]\par\medskip + \textbf{\ldots{}}\textbf{Na6} \textbf{9.} \textbf{Ba3} \textbf{c6} \textbf{10.} \textbf{Qd3} \textbf{Rb8} \textbf{11.} \textbf{e4} \textbf{fxe4} \textbf{12.} \textbf{Nxe4} \textbf{Bf5} \textbf{13.} \textbf{Nxf6+} \textbf{Bxf6} \textbf{14.} \textbf{Qd2} \textbf{Nc7} \textbf{15.} \textbf{Rae1} \textbf{Qd7} \textbf{16.} \textbf{h4} \textbf{b5} \textbf{17.} \textbf{Re3} \textbf{bxc4} \textbf{18.} \textbf{bxc4} \textbf{Bh3} \textbf{19.} \textbf{Rfe1} \textbf{Bxg2} \newline last book move\par \textbf{20.} \textbf{Kxg2} \textbf{Qf5} \textbf{21.} \textbf{Re4} \textbf{Rbe8} \textbf{22.} \textbf{Rf4} \textbf{Qc8} \textbf{23.} \textbf{Qa5} \textbf{d5} \textbf{24.} \textbf{cxd5} \textbf{Nxd5} \textbf{25.} \textbf{Rfe4} \newline e7 seems the pivot of the position\par \textbf{\ldots{}Qf5} \textbf{26.} \textbf{Qd2} ( 26. Qxa7 ??{} taking the pawn is naive Qxf3+ Annihilates a defender: f3 27. Kxf3 Bxd4+ 28. Kg4 Bxa7 29. Bxe7 Rxf2 -+{} ( 29... Rxe7 30. Rxe7 Bxf2 31. a4 -+{} ) ( 29... Nxe7 ?!{} 30. Rxe7 Rxe7 31. Rxe7 Bxf2 32. a4 -+{} ) ) \textbf{Bg7} \textbf{27.} \textbf{Nh2} \textbf{Rf7} \textbf{28.} \textbf{Bc5} \newline The white bishop on an outpost\par \textbf{\ldots{}Qd7} \textbf{29.} \textbf{a4} \textbf{Nf6} \textbf{30.} \textbf{Re5} \textbf{Nd5} \textbf{31.} \textbf{R5e4} ( 31. R5e2 Rb8 \wbetter{} ) \textbf{Nf6} {} \textbf{32.} \textbf{Re5} \textbf{Kh8} ( 32... Nd5 33. R5e2 \wbetter{} ) \textbf{33.} \textbf{Kg1} \textbf{Nd5} \newline A valuable piece\par \textbf{34.} \textbf{R5e2} \textbf{a6} \textbf{35.} \textbf{Qd3} \textbf{Qh3} ( 35... Ra8 \wbetter{} ) \textbf{36.} \textbf{Nf3} ( 36. Qxa6 Ref8 37. Qa5 \wbetter{} ( 37. Qxc6 Rxf2 38. Rxf2 Qxg3+ 39. Kh1 Rxf2 ( 39... Qxf2 ?!{} 40. Rf1 Qa2 41. Rxf8+ Bxf8 42. Kg1 {} ) 40. Qa8+ Bf8 41. Qxf8+ Rxf8 42. Re2 Rf2 43. Rxf2 Qxf2 44. Ng4 Qf1+ 45. Kh2 Nf4 46. Ne3 Qf2+ 47. Kh1 Qxh4+ 48. Kg1 Qg3+ 49. Kf1 Qxe3 50. Bb6 Qe2+ 51. Kg1 Qg2\# ) ( 37. Bxe7 ??{} White will choke on that pawn Rxf2 ( 37... Nxe7 ?!{} 38. Rd1 -+{} ) 38. Rxf2 Qxg3+ 39. Kh1 Rxf2 ( 39... Qxf2 40. Re2 Qxd4 41. Bxf8 Qd1+ 42. Kg2 Nf4+ 43. Kf3 {} ) 40. Qc8+ Bf8 41. Bf6+ Nxf6 42. Qxf8+ Ng8 43. Qxf2 Qxf2 -+{} ) ) \textbf{Nf4} \bbetter{} \newline Do you see the mate threat?\par \textbf{37.} \textbf{gxf4} \textbf{Rxf4} \newline The mate threat is Rg4\par \textbf{38.} \textbf{Ne5} ( 38. Re4 !?{} is worthy of consideration Qg4+ 39. Kf1 Rxf3 40. Rxg4 Rxd3 41. Re6 \bbetter{} ) \textbf{Rg4+} !{} \ensuremath{\mp}{} \newline keeping the advantage\par \textbf{39.} \textbf{Nxg4} \newline Theme: Deflection from d3\par \textbf{\ldots{}Qxd3} \textbf{40.} \textbf{Re4} \textbf{Qf3} \textbf{41.} \textbf{Nh2} \textbf{Qf5} \textbf{42.} \textbf{Bxe7} ( 42. Kg2 Rf8 43. f3 Bf6 -+{} ) \textbf{Kg8} \textbf{43.} \textbf{f3} ( 43. R1e3 Qd5 -+{} ) \textbf{Qh3} -+{} \textbf{44.} \textbf{R1e2} ( 44. Kh1 -+{} ) \textbf{Qg3+} \textbf{45.} \textbf{Rg2} \textbf{Bxd4+} !{} \newline a devastating blow\par \textbf{46.} \textbf{Kh1} ( 46. Rxd4 A deflection Qe1+ Theme: Double Attack ) \textbf{Qh3} \textbf{47.} \textbf{Rgg4} ( 47. a5 Bc3 -+{} ) \textbf{c5} \textbf{48.} \textbf{h5} ( 48. Re1 does not improve anything Bf6 49. Rge4 Bxe7 50. Rxe7 Rxe7 51. Rxe7 Qxh4 -+{} ) \textbf{Rb8} \textbf{49.} \textbf{Re1} \textbf{Be5} \textbf{50.} \textbf{Rh4} \textbf{Qf5} \textbf{51.} \textbf{hxg6} \textbf{hxg6} \textbf{52.} \textbf{Re2} ( 52. Bxc5 doesn't change the outcome of the game Bg3 53. Rb4 Rc8 -+{} ( 53... Bxe1 ?!{} is clearly worse 54. Rxb8+ Kh7 55. Rb7+ Kh8 56. Rb8+ Kg7 57. Rb7+ Kf6 58. Ng4+ Kg5 59. Be3+ Kh4 60. Kg2 Qc2+ 61. Nf2 \bbetter{} ) ( 53... Qxc5 ?!{} is much worse 54. Rxb8+ Bxb8 55. Re8+ Kf7 56. Rxb8 -+{} ) ) \textbf{Rb1+} ( 52... Qb1+ !?{} keeps an even firmer grip 53. Kg2 Rb2 54. Rxb2 Qxb2+ 55. Kg1 -+{} ) \textbf{53.} \textbf{Kg2} \textbf{Bd4} \textbf{54.} \textbf{Ng4} ( 54. Rxd4 no good, but what else? cxd4 55. Ng4 -+{} ) \textbf{Rg1+} \textbf{55.} \textbf{Kh2} ( 55. Kh3 doesn't do any good Qxf3+ 56. Kh2 Rh1\# ) \textbf{Qf4+} ( 55... Qf4+ 56. Kh3 Qg3\# ) \textbf{0-1}|} + in + ae expected actual; + close_in ic + +let test_pgn_with_clock_to_tex _ctxt = + let filename = "pgn_examples/2.pgn" in + let ic = In_channel.open_text filename in + let input_string = In_channel.input_all ic in + let actual = Pgn2tex.to_tex input_string ~diagram_data ~clock:true in + let expected = + {|\title{Casual Rapid game} +\author{montillo2015 (1919)\\reassessyourchess (1869)} +\date{2023.12.03, https://lichess.org/9Z0taHcG}\maketitle +\newchessgame +\textbf{1.} \textbf{d4} \textbf{1...} \textbf{Nf6} \textbf{2.} \textbf{Nf3} \textbf{2...} \textbf{d5} \textbf{3.} \textbf{e3}\par\medskip\noindent\begin{minipage}{\linewidth}0:10:01\par\nopagebreak\smallskip +\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\nopagebreak\vspace{1em} +0:10:00\end{minipage}\par\medskip \textbf{3...} \textbf{e6}\par\medskip\noindent\begin{minipage}{\linewidth}0:10:01\par\nopagebreak\smallskip +\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 3, vmargin=false]\par\nopagebreak\vspace{1em} +0:10:01\end{minipage}\par\medskip \newline D05 Queen's Pawn Game: Colle System\par \textbf{4.} \textbf{a3} \textbf{4...} \textbf{c5} \textbf{5.} \textbf{c4} \textbf{5...} \textbf{Nc6}\par\medskip\noindent\begin{minipage}{\linewidth}0:10:02\par\nopagebreak\smallskip +\chessboard[setfen=r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 3 5, vmargin=false]\par\nopagebreak\vspace{1em} +0:10:02\end{minipage}\par\medskip \textbf{6.} \textbf{dxc5} \textbf{6...} \textbf{Bxc5} \textbf{7.} \textbf{b4} \textbf{7...} \textbf{Be7} \textbf{8.} \textbf{Bb2}\par\medskip\noindent\begin{minipage}{\linewidth}0:10:00\par\nopagebreak\smallskip +\chessboard[setfen=r1bqkb1r/pp1ppppp/2n5/2p5/4P3/2N2N2/PPPP1PPP/R1BQKB1R b KQkq - 5 7, vmargin=false]\par\nopagebreak\vspace{1em} +0:10:03\end{minipage}\par\medskip \textbf{8...} \textbf{dxc4} \textbf{9.} \textbf{Qxd8+} \textbf{9...} \textbf{Nxd8} \textbf{10.} \textbf{Bxc4} \textbf{10...} \textbf{O-O} \textbf{11.} \textbf{O-O} \textbf{11...} \textbf{a6} \textbf{12.} \textbf{Nc3} \textbf{12...} \textbf{b5} \textbf{13.} \textbf{Be2} \textbf{13...} \textbf{Bb7} \textbf{14.} \textbf{Nd4} \textbf{14...} \textbf{Ne4} \textbf{15.} \textbf{Nxe4} \textbf{15...} \textbf{Bxe4} \textbf{16.} \textbf{Bf3} \textbf{16...} \textbf{Bxf3} \textbf{17.} \textbf{Nxf3} \textbf{17...} \textbf{Nc6} \textbf{18.} \textbf{Rac1} \textbf{18...} \textbf{Rac8} \textbf{19.} \textbf{Rfd1} \textbf{19...} \textbf{Rfd8} \textbf{20.} \textbf{Rxd8+} \textbf{20...} \textbf{Rxd8} \textbf{21.} \textbf{g3} \textbf{21...} \textbf{Nb8} \textbf{22.} \textbf{Rc7} \textbf{22...} \textbf{Bd6} \textbf{23.} \textbf{Rc2} \textbf{23...} \textbf{f6} \textbf{24.} \textbf{Kg2} \textbf{24...} \textbf{e5} \textbf{25.} \textbf{h4} \textbf{25...} \textbf{e4} \textbf{26.} \textbf{Nd4} \textbf{26...} \textbf{Kf7} \textbf{27.} \textbf{Nf5} \textbf{27...} \textbf{Nd7} \textbf{28.} \textbf{Nxd6+} \newline Black resigns.\par \textbf{1-0}|} + in + ae expected actual; + close_in ic + +let test_pgn_with_unsupported_text _ctxt = + let filename = "pgn_examples/4.pgn" in + let ic = In_channel.open_text filename in + let input_string = In_channel.input_all ic in + let actual = Pgn2tex.to_tex input_string ~diagram_data ~clock:true in + (* let expected = + {|\title{Casual Rapid game} +\author{montillo2015 (1919) vs. reassessyourchess (1869)} +\date{2023.12.03, https://lichess.org/9Z0taHcG}\maketitle +\newchessgame +\textbf{1.} \textbf{d4} \textbf{1...} \textbf{Nf6} \textbf{2.} \textbf{Nf3} \textbf{2...} \textbf{d5} \textbf{3.} \textbf{e3}\par\nobreak 0:10:01\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:00\par \textbf{3...} \textbf{e6}\par\nobreak 0:10:01\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:01\par \newline D05 Queen's Pawn Game: Colle System\par \textbf{4.} \textbf{a3} \textbf{4...} \textbf{c5} \textbf{5.} \textbf{c4} \textbf{5...} \textbf{Nc6}\par\nobreak 0:10:02\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:02\par \textbf{6.} \textbf{dxc5} \textbf{6...} \textbf{Bxc5} \textbf{7.} \textbf{b4} \textbf{7...} \textbf{Be7} \textbf{8.} \textbf{Bb2}\par\nobreak 0:10:00\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:03\par \textbf{8...} \textbf{dxc4} \textbf{9.} \textbf{Qxd8+} \textbf{9...} \textbf{Nxd8} \textbf{10.} \textbf{Bxc4} \textbf{10...} \textbf{O-O} \textbf{11.} \textbf{O-O} \textbf{11...} \textbf{a6} \textbf{12.} \textbf{Nc3} \textbf{12...} \textbf{b5} \textbf{13.} \textbf{Be2} \textbf{13...} \textbf{Bb7} \textbf{14.} \textbf{Nd4} \textbf{14...} \textbf{Ne4} \textbf{15.} \textbf{Nxe4} \textbf{15...} \textbf{Bxe4} \textbf{16.} \textbf{Bf3} \textbf{16...} \textbf{Bxf3} \textbf{17.} \textbf{Nxf3} \textbf{17...} \textbf{Nc6} \textbf{18.} \textbf{Rac1} \textbf{18...} \textbf{Rac8} \textbf{19.} \textbf{Rfd1} \textbf{19...} \textbf{Rfd8} \textbf{20.} \textbf{Rxd8+} \textbf{20...} \textbf{Rxd8} \textbf{21.} \textbf{g3} \textbf{21...} \textbf{Nb8} \textbf{22.} \textbf{Rc7} \textbf{22...} \textbf{Bd6} \textbf{23.} \textbf{Rc2} \textbf{23...} \textbf{f6} \textbf{24.} \textbf{Kg2} \textbf{24...} \textbf{e5} \textbf{25.} \textbf{h4} \textbf{25...} \textbf{e4} \textbf{26.} \textbf{Nd4} \textbf{26...} \textbf{Kf7} \textbf{27.} \textbf{Nf5} \textbf{27...} \textbf{Nd7} \textbf{28.} \textbf{Nxd6+} \newline Black resigns.\par \textbf{1-0}|} *) + (* in + ae expected actual; *) + print_endline actual; + close_in ic + +let test_pgn_with_custom_headers _ctxt = + let filename = "pgn_examples/7.pgn" in + let ic = In_channel.open_text filename in + let input_string = In_channel.input_all ic in + let actual = Pgn2tex.to_tex input_string ~diagram_data ~clock:false in + (* let expected = + {|\title{Casual Rapid game} +\author{montillo2015 (1919) vs. reassessyourchess (1869)} +\date{2023.12.03, https://lichess.org/9Z0taHcG}\maketitle +\newchessgame +\textbf{1.} \textbf{d4} \textbf{1...} \textbf{Nf6} \textbf{2.} \textbf{Nf3} \textbf{2...} \textbf{d5} \textbf{3.} \textbf{e3}\par\nobreak 0:10:01\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:00\par \textbf{3...} \textbf{e6}\par\nobreak 0:10:01\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:01\par \newline D05 Queen's Pawn Game: Colle System\par \textbf{4.} \textbf{a3} \textbf{4...} \textbf{c5} \textbf{5.} \textbf{c4} \textbf{5...} \textbf{Nc6}\par\nobreak 0:10:02\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:02\par \textbf{6.} \textbf{dxc5} \textbf{6...} \textbf{Bxc5} \textbf{7.} \textbf{b4} \textbf{7...} \textbf{Be7} \textbf{8.} \textbf{Bb2}\par\nobreak 0:10:00\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:03\par \textbf{8...} \textbf{dxc4} \textbf{9.} \textbf{Qxd8+} \textbf{9...} \textbf{Nxd8} \textbf{10.} \textbf{Bxc4} \textbf{10...} \textbf{O-O} \textbf{11.} \textbf{O-O} \textbf{11...} \textbf{a6} \textbf{12.} \textbf{Nc3} \textbf{12...} \textbf{b5} \textbf{13.} \textbf{Be2} \textbf{13...} \textbf{Bb7} \textbf{14.} \textbf{Nd4} \textbf{14...} \textbf{Ne4} \textbf{15.} \textbf{Nxe4} \textbf{15...} \textbf{Bxe4} \textbf{16.} \textbf{Bf3} \textbf{16...} \textbf{Bxf3} \textbf{17.} \textbf{Nxf3} \textbf{17...} \textbf{Nc6} \textbf{18.} \textbf{Rac1} \textbf{18...} \textbf{Rac8} \textbf{19.} \textbf{Rfd1} \textbf{19...} \textbf{Rfd8} \textbf{20.} \textbf{Rxd8+} \textbf{20...} \textbf{Rxd8} \textbf{21.} \textbf{g3} \textbf{21...} \textbf{Nb8} \textbf{22.} \textbf{Rc7} \textbf{22...} \textbf{Bd6} \textbf{23.} \textbf{Rc2} \textbf{23...} \textbf{f6} \textbf{24.} \textbf{Kg2} \textbf{24...} \textbf{e5} \textbf{25.} \textbf{h4} \textbf{25...} \textbf{e4} \textbf{26.} \textbf{Nd4} \textbf{26...} \textbf{Kf7} \textbf{27.} \textbf{Nf5} \textbf{27...} \textbf{Nd7} \textbf{28.} \textbf{Nxd6+} \newline Black resigns.\par \textbf{1-0}|} *) + (* in + ae expected actual; *) + print_endline actual; + close_in ic + +let test_pgn_with_different_unsupported_text _ctxt = + let filename = "pgn_examples/10.pgn" in + let ic = In_channel.open_text filename in + let input_string = In_channel.input_all ic in + let actual = Pgn2tex.to_tex input_string ~diagram_data ~clock:false in + (* let expected = + {|\title{Casual Rapid game} +\author{montillo2015 (1919) vs. reassessyourchess (1869)} +\date{2023.12.03, https://lichess.org/9Z0taHcG}\maketitle +\newchessgame +\textbf{1.} \textbf{d4} \textbf{1...} \textbf{Nf6} \textbf{2.} \textbf{Nf3} \textbf{2...} \textbf{d5} \textbf{3.} \textbf{e3}\par\nobreak 0:10:01\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:00\par \textbf{3...} \textbf{e6}\par\nobreak 0:10:01\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:01\par \newline D05 Queen's Pawn Game: Colle System\par \textbf{4.} \textbf{a3} \textbf{4...} \textbf{c5} \textbf{5.} \textbf{c4} \textbf{5...} \textbf{Nc6}\par\nobreak 0:10:02\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:02\par \textbf{6.} \textbf{dxc5} \textbf{6...} \textbf{Bxc5} \textbf{7.} \textbf{b4} \textbf{7...} \textbf{Be7} \textbf{8.} \textbf{Bb2}\par\nobreak 0:10:00\par\nobreak\chessboard[setfen=rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 3, vmargin=false]\par\medskip\nobreak\ 0:10:03\par \textbf{8...} \textbf{dxc4} \textbf{9.} \textbf{Qxd8+} \textbf{9...} \textbf{Nxd8} \textbf{10.} \textbf{Bxc4} \textbf{10...} \textbf{O-O} \textbf{11.} \textbf{O-O} \textbf{11...} \textbf{a6} \textbf{12.} \textbf{Nc3} \textbf{12...} \textbf{b5} \textbf{13.} \textbf{Be2} \textbf{13...} \textbf{Bb7} \textbf{14.} \textbf{Nd4} \textbf{14...} \textbf{Ne4} \textbf{15.} \textbf{Nxe4} \textbf{15...} \textbf{Bxe4} \textbf{16.} \textbf{Bf3} \textbf{16...} \textbf{Bxf3} \textbf{17.} \textbf{Nxf3} \textbf{17...} \textbf{Nc6} \textbf{18.} \textbf{Rac1} \textbf{18...} \textbf{Rac8} \textbf{19.} \textbf{Rfd1} \textbf{19...} \textbf{Rfd8} \textbf{20.} \textbf{Rxd8+} \textbf{20...} \textbf{Rxd8} \textbf{21.} \textbf{g3} \textbf{21...} \textbf{Nb8} \textbf{22.} \textbf{Rc7} \textbf{22...} \textbf{Bd6} \textbf{23.} \textbf{Rc2} \textbf{23...} \textbf{f6} \textbf{24.} \textbf{Kg2} \textbf{24...} \textbf{e5} \textbf{25.} \textbf{h4} \textbf{25...} \textbf{e4} \textbf{26.} \textbf{Nd4} \textbf{26...} \textbf{Kf7} \textbf{27.} \textbf{Nf5} \textbf{27...} \textbf{Nd7} \textbf{28.} \textbf{Nxd6+} \newline Black resigns.\par \textbf{1-0}|} *) + (* in + ae expected actual; *) + print_endline actual; + close_in ic + +let suite = + "pgn2tex tests" + >::: [ + "basic_pgn_to_tex" >:: test_basic_pgn_to_tex; + "pgn_with_clock_to_tex" >:: test_pgn_with_clock_to_tex; + (* "pgn_with_unsupported_text" >:: test_pgn_with_unsupported_text; *) + (* "pgn_with_custom_headers" >:: test_pgn_with_custom_headers; *) + "pgn_with_different_unsupported_text" + >:: test_pgn_with_different_unsupported_text; + ] + +let () = run_test_tt_main suite diff --git a/test/test_pgn_to_tex.ml b/test/test_pgn_to_tex.ml deleted file mode 100644 index e69de29..0000000 diff --git a/test/test_suite.ml b/test/test_suite.ml new file mode 100644 index 0000000..3309e3c --- /dev/null +++ b/test/test_suite.ml @@ -0,0 +1,13 @@ +open OUnit2 + +let () = + let all = + "all tests" + >::: [ + Test_lexer.suite; + Test_latex.suite; + Test_pgn2tex.suite; + Test_nag.suite; + ] + in + run_test_tt_main all