Rob Herbison
Northeastern University
November 29, 2017
This is being actively modified so right now it is a lot of nonsense.
This is a demonstration of the methodology of Markov chain analysis on chess games. The goal is to uncover patterns in historical chess games. This is one experiment out of many possible using Markov chain analysis.
A chess game can be broken down into a collection of board positions including their proceeding move. While the total number of these states available through legal moves is large, the space can be partitioned into more manageable states. These states are categorical and non-ordinal making them difficult to work with using many established statistical methods. A Markov chain analysis works well with these data sets and is effective at uncovering relationships between two nearby states.
The state space is {The Initial Board Position, Moves with a Capture and no Check, Moves with a Check and no Capture, Moves with Both Capture and Check, All Else}. Call these {I, X, P, A, E} for easy reference. This space was chosen because it is manageable and it describes some of the more notable events in a game that are easily measured.
By the rules of chess, the initial state, I, is transient. The board position can be subsequently reached legally but, by design, that involves a move which changes the nature of the state. In competitive games it is unlikely the initial board position will ever be reached a second time.
The null hypothesis is that the probabilities of both captures and checks are independent of whether captures or checks occurred in the previous move. The goal is to uncover clumping around captures in the game. Finally, the intent is to leave all of my methodology scalable to allow the same code to work on a much larger collection of chess algebra.
Here is a sample of the data from the first game:
x <- scan("C:/Users/robhe/Dropbox/Markov_Chess/Kasparov_Deep_Blue.txt", what="", sep="\n")
y <- strsplit(x, "[[:space:]]+")
y[[1]]
## [1] "1.e4" "c5" "2.c3" "d5" "3.exd5" "Qxd5"
## [7] "4.d4" "Nf6" "5.Nf3" "Bg4" "6.Be2" "e6"
## [13] "7.h3" "Bh5" "8.0-0" "Nc6" "9.Be3" "cxd4"
## [19] "10.cxd4" "Bb4" "11.a3" "Ba5" "12.Nc3" "Qd6"
## [25] "13.Nb5" "Qe7" "14.Ne5" "Bxe2" "15.Qxe2" "0-0"
## [31] "16.Rac1" "Rac8" "17.Bg5" "Bb6" "18.Bxf6" "gxf6"
## [37] "19.Nc4" "Rfd8" "20.Nxb6" "axb6" "21.Rfd1" "f5"
## [43] "22.Qe3" "Qf6" "23.d5" "Rxd5" "24.Rxd5" "exd5"
## [49] "25.b3" "Kh8" "26.Qxb6" "Rg8" "27.Qc5" "d4"
## [55] "28.Nd6" "f4" "29.Nxb7" "Ne5" "30.Qd5" "f3"
## [61] "31.g3" "Nd3" "32.Rc7" "Re8" "33.Nd6" "Re1+"
## [67] "34.Kh2" "Nxf2" "35.Nxf7+" "Kg7" "36.Ng5+" "Kh6"
## [73] "37.Rxh7+"
I used R to read the text files and create the nested vectors holding the moves.
You will notice that I do not use the normal convention for numbering chess games. For computational purposes I count each piece moved as one move. Absent from the data, it is assumed that the board always starts at the initial state. This is the 0th state. By the rules of chess and under my partition of the possible board positions, the zeroth move will always go to the else category which is consistent with the data. My methodology assumes this to ease computing.
A note on chess algebra: Each entry represents moves with the numbered entries representing white’s turn. They appear to give incomplete information but it is standard to remove any characters not necessary to prevent ambiguity. In the case of both captures and checks the symbols are left in for clarity. If an “x” is in the entry, a piece was taken. If a “+” is in the entry, the move left a king in check. Interesting Markov chain analyses on some other state spaces are more difficult due to the peculiarities of chess algebra.
Below are functions to determine what characteristics are happening at each entry. These form truth tables of the same dimensions as the chess data entries. Note that only oCapture, oCheck, Both, and Else correspond to the state space.
States <- c("Initial", "oCapture", "oCheck", "Both", "Else")
isX <- function(y) {
n <- 1:length(y)
X <- c()
for(game in n) {X[[game]] <- grepl("x", y[[game]], fixed = TRUE)}
return(X)
}
isP <- function(y) {
n <- 1:length(y)
P <- c()
for(game in n) {P[[game]] <- grepl("+", y[[game]], fixed = TRUE)}
return(P)
}
Captures <- isX(y)
Checks <- isP(y)
isoX <- function(y) {
n <- 1:length(y)
oX <- c()
for(game in n) {oX[[game]] <- Captures[[game]] & (! Checks[[game]])}
return(oX)
}
isoP <- function(y) {
n <- 1:length(y)
oP <- c()
for(game in n) {oP[[game]] <- Checks[[game]] & (! Captures[[game]])}
return(oP)
}
isA <- function(y) {
n <- 1:length(y)
A <- c()
for(game in n) {A[[game]] <- Captures[[game]] & Checks[[game]]}
return(A)
}
isE <- function(y) {
n <- 1:length(y)
E <- c()
for(game in n) {E[[game]] <- ! (Captures[[game]] | Checks[[game]])}
return(E)
}
oCaptures <- isoX(y)
oChecks <- isoP(y)
Both <- isA(y)
Else <- isE(y)
The empirical data is a vector that counts how many times the games existed in each state. The count vector is very similar, except it does not count the last entry in each game. We do this because there is no state that follows the last entry. The count vector is used to build the Markov chain transition matrix.
EmpVector <- function(y) {
EV <- c()
length(y) -> EV[1]
temp2 <- c()
temp3 <- c()
temp4 <- c()
temp5 <- c()
n <- 1:length(y)
for(game in n) {
sum(oCaptures[[game]], na.rm = TRUE) -> temp2[[game]]
sum(oChecks[[game]], na.rm = TRUE) -> temp3[[game]]
sum(Both[[game]], na.rm = TRUE) -> temp4[[game]]
sum(Else[[game]], na.rm = TRUE) -> temp5[[game]]}
sum(temp2) -> EV[2]
sum(temp3) -> EV[3]
sum(temp4) -> EV[4]
sum(temp5) -> EV[5]
EV <- EV/sum(EV)
return(EV)
}
#I can simplify these functions...
CountVector <- function(y) {
CV <- c()
length(y) -> CV[1]
temp2 <- c()
temp3 <- c()
temp4 <- c()
temp5 <- c()
n <- 1:length(y)
len <- c()
for(game in n) {
len <- 1:(length(y[[game]])-1)
sum(oCaptures[[game]][len], na.rm = TRUE) -> temp2[[game]]
sum(oChecks[[game]][len], na.rm = TRUE) -> temp3[[game]]
sum(Both[[game]][len], na.rm = TRUE) -> temp4[[game]]
sum(Else[[game]][len], na.rm = TRUE) -> temp5[[game]]}
sum(temp2) -> CV[2]
sum(temp3) -> CV[3]
sum(temp4) -> CV[4]
sum(temp5) -> CV[5]
return(CV)
}
EV <- EmpVector(y)
CV <- CountVector(y)
The Empirical Data: P(a game at time n≥0 will be in each state)
EV
## [1] 0.01084991 0.17902351 0.03164557 0.01265823 0.76582278
For the purpose of creating a transition matrix I defined a count vector. This defines the number of times the games were in each state before a move. It truncates off the states that occurred last in each game. It is not normalized like the empirical data so it can not be seen as a probability distribution.
The Count Vector:
CV
## [1] 12 198 35 13 836
P(a game at time (n+1)≥1 will be in state j | the game is in state i at time n)
As a note, the notation “oCapture” and “oCheck” is used to differentiate these states from the states including all captures or checks.
T <- matrix(1:25, nrow=5, ncol = 5, dimnames = list(States, States))
T[,1] <- 0
T[1,] <- 0
T[1,5] <- 1
MatrixEntry <- function(i, j, count) {
k <- 0
n <- 1:length(y)
for(game in n) {for(entry in 1:(length(i[[game]])-1)) {
if (i[[game]][entry] & j[[game]][entry+1]) {k=k+1}}}
return(k/count)
}
###This can be simplified with a loop
T[2,2] = MatrixEntry(oCaptures, oCaptures, CV[2])
T[2,3] = MatrixEntry(oCaptures, oChecks, CV[2])
T[2,4] = MatrixEntry(oCaptures, Both, CV[2])
T[2,5] = MatrixEntry(oCaptures, Else, CV[2])
T[3,2] = MatrixEntry(oChecks, oCaptures, CV[3])
T[3,3] = MatrixEntry(oChecks, oChecks, CV[3])
T[3,4] = MatrixEntry(oChecks, Both, CV[3])
T[3,5] = MatrixEntry(oChecks, Else, CV[3])
T[4,2] = MatrixEntry(Both, oCaptures, CV[4])
T[4,3] = MatrixEntry(Both, oChecks, CV[4])
T[4,4] = MatrixEntry(Both, Both, CV[4])
T[4,5] = MatrixEntry(Both, Else, CV[4])
T[5,2] = MatrixEntry(Else, oCaptures, CV[5])
T[5,3] = MatrixEntry(Else, oChecks, CV[5])
T[5,4] = MatrixEntry(Else, Both, CV[5])
T[5,5] = MatrixEntry(Else, Else, CV[5])
#Quick and dirty
T
## Initial oCapture oCheck Both Else
## Initial 0 0.00000000 0.00000000 0.000000000 1.0000000
## oCapture 0 0.44949495 0.02020202 0.030303030 0.5000000
## oCheck 0 0.05714286 0.00000000 0.000000000 0.9428571
## Both 0 0.69230769 0.00000000 0.076923077 0.2307692
## Else 0 0.11722488 0.03708134 0.008373206 0.8373206
SV <- function(Matrix, Labels=TRUE) {
sv <- c()
sv <- eigen(t(Matrix))$vectors[,1]
sv <- sv/sum(sv)
Out <- sv %*% Matrix
#This adds back in headers and will throw a bad answer if the function fails.
if (Labels==TRUE) {return(Out)}
else {return(sv)}
}
sv <- SV(T,FALSE)
StationaryVector <- SV(T,TRUE)
StationaryVector
## Initial oCapture oCheck Both Else
## [1,] 0 0.1838717 0.03229699 0.01302813 0.7708031
I also ran a test on the stationary vector. I multiplied the vector by the transition matrix to confirm that the two were equal.
SV(T,TRUE) == SV(T,TRUE) %*% T
## Initial oCapture oCheck Both Else
## [1,] TRUE TRUE TRUE TRUE TRUE
# This operation happens to work in this case because R has excelent numeric handeling.
# It will not work if Labels = False because it pulls R out of the matrix tools.
# This code may not work in other languages and possibly with other inputs unless it is rounded.
Stationary Distribution: By transposing the matrix and taking the first eigen vector I get a multiple of the stationary distribution. I then normalize the vector and test it by multiplying it through the original matrix. Because the truncated matrix is normal, we know that our stationary vector is unique.
You can see below that the stationary vector is very similar to the empirical data. The most significant difference is in the initial state. The initial state will not show up in the stationary vector because it is transient.
barplot(EV, names.arg = States, ylab = "Probaility", main = "Empirical Data Distribution")
barplot(StationaryVector, main = "Stationary Distribution")
As expected, the initial state was the only transient state in this data set. This can be seen in this unweighted digraph below. It is also clear that state reaches every other state or itself. Excluding the initial state, this is likely an artifact of the low sample size.
#install.packages("igraph")
library(igraph)
## Warning: package 'igraph' was built under R version 3.3.3
##
## Attaching package: 'igraph'
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
TGraph <- graph_from_adjacency_matrix(T, weighted=TRUE)
#E(TGraph)$width <- 7*(E(TGraph)$weight)
#as.directed(TGraph, mode = c("mutual", "arbitrary"))
plot(TGraph, main = 'Unweighted Transitions in a Chess Game')
This Markov chain is normal, as several states return to themselves. It has only one stationary class, in addition to the transient initial state. It is asymmetric and has a unique stationary distribution. If we took a random walk on this weighted graph the stationary distribution would represent the long run probabilities of being in each state. As shown above, the stationary distribution closely matches the empirical probabilities we found of the game being in each state.
This Markov model is clearly a dramatically simplified model of chess. It strips out the vast majority of the information available in the raw chess algebra. However, the Markov chain does give convincing answers . . . . .
This is the record of two historic six game chess matches played between Gary Kasparov, the raining world chess champion, and Deep Blue, a super computer developed by IBM. The combination of these two matches has been widely regarded in popular literature as a turning point in the relationship between human and artificial intelligence.
All chess algebra was taken from Wikipedia’s account of the games.
https://en.wikipedia.org/wiki/Deep_Blue_versus_Garry_Kasparov
The conventions for interpreting the algebra are widely standardized but I used the World Chess Federation’s (FIDE) Handbook, Section E, Laws of Chess.
https://www.fide.com/component/handbook/
Below is the chess algebra for the first game as a text file.
Deep Blue–Kasparov
1.e4 c5 2.c3 d5 3.exd5 Qxd5 4.d4 Nf6 5.Nf3 Bg4 6.Be2 e6 7.h3 Bh5 8.0-0 Nc6 9.Be3 cxd4 10.cxd4 Bb4 11.a3 Ba5 12.Nc3 Qd6 13.Nb5 Qe7 14.Ne5 Bxe2 15.Qxe2 0-0 16.Rac1 Rac8 17.Bg5 Bb6 18.Bxf6 gxf6 19.Nc4 Rfd8 20.Nxb6 axb6 21.Rfd1 f5 22.Qe3 Qf6 23.d5 Rxd5 24.Rxd5 exd5 25.b3 Kh8 26.Qxb6 Rg8 27.Qc5 d4 28.Nd6 f4 29.Nxb7 Ne5 30.Qd5 f3 31.g3 Nd3 32.Rc7 Re8 33.Nd6 Re1+ 34.Kh2 Nxf2 35.Nxf7+ Kg7 36.Ng5+ Kh6 37.Rxh7+ 1–0
This is the cleaned dataset as nested vectors showing all twelve games for reference. Notice that some elements including the score and the header have been removed.
y
## [[1]]
## [1] "1.e4" "c5" "2.c3" "d5" "3.exd5" "Qxd5"
## [7] "4.d4" "Nf6" "5.Nf3" "Bg4" "6.Be2" "e6"
## [13] "7.h3" "Bh5" "8.0-0" "Nc6" "9.Be3" "cxd4"
## [19] "10.cxd4" "Bb4" "11.a3" "Ba5" "12.Nc3" "Qd6"
## [25] "13.Nb5" "Qe7" "14.Ne5" "Bxe2" "15.Qxe2" "0-0"
## [31] "16.Rac1" "Rac8" "17.Bg5" "Bb6" "18.Bxf6" "gxf6"
## [37] "19.Nc4" "Rfd8" "20.Nxb6" "axb6" "21.Rfd1" "f5"
## [43] "22.Qe3" "Qf6" "23.d5" "Rxd5" "24.Rxd5" "exd5"
## [49] "25.b3" "Kh8" "26.Qxb6" "Rg8" "27.Qc5" "d4"
## [55] "28.Nd6" "f4" "29.Nxb7" "Ne5" "30.Qd5" "f3"
## [61] "31.g3" "Nd3" "32.Rc7" "Re8" "33.Nd6" "Re1+"
## [67] "34.Kh2" "Nxf2" "35.Nxf7+" "Kg7" "36.Ng5+" "Kh6"
## [73] "37.Rxh7+"
##
## [[2]]
## [1] "1.Nf3" "d5" "2.d4" "e6" "3.g3" "c5" "4.Bg2"
## [8] "Nc6" "5.0-0" "Nf6" "6.c4" "dxc4" "7.Ne5" "Bd7"
## [15] "8.Na3" "cxd4" "9.Naxc4" "Bc5" "10.Qb3" "0-0" "11.Qxb7"
## [22] "Nxe5" "12.Nxe5" "Rb8" "13.Qf3" "Bd6" "14.Nc6" "Bxc6"
## [29] "15.Qxc6" "e5" "16.Rb1" "Rb6" "17.Qa4" "Qb8" "18.Bg5"
## [36] "Be7" "19.b4" "Bxb4" "20.Bxf6" "gxf6" "21.Qd7" "Qc8"
## [43] "22.Qxa7" "Rb8" "23.Qa4" "Bc3" "24.Rxb8" "Qxb8" "25.Be4"
## [50] "Qc7" "26.Qa6" "Kg7" "27.Qd3" "Rb8" "28.Bxh7" "Rb2"
## [57] "29.Be4" "Rxa2" "30.h4" "Qc8" "31.Qf3" "Ra1" "32.Rxa1"
## [64] "Bxa1" "33.Qh5" "Qh8" "34.Qg4+" "Kf8" "35.Qc8+" "Kg7"
## [71] "36.Qg4+" "Kf8" "37.Bd5" "Ke7" "38.Bc6" "Kf8" "39.Bd5"
## [78] "Ke7" "40.Qf3" "Bc3" "41.Bc4" "Qc8" "42.Qd5" "Qe6"
## [85] "43.Qb5" "Qd7" "44.Qc5+" "Qd6" "45.Qa7+" "Qd7" "46.Qa8"
## [92] "Qc7" "47.Qa3+" "Qd6" "48.Qa2" "f5" "49.Bxf7" "e4"
## [99] "50.Bh5" "Qf6" "51.Qa3+" "Kd7" "52.Qa7+" "Kd8" "53.Qb8+"
## [106] "Kd7" "54.Be8+" "Ke7" "55.Bb5" "Bd2" "56.Qc7+" "Kf8"
## [113] "57.Bc4" "Bc3" "58.Kg2" "Be1" "59.Kf1" "Bc3" "60.f4"
## [120] "exf3" "61.exf3" "Bd2" "62.f4" "Ke8" "63.Qc8+" "Ke7"
## [127] "64.Qc5+" "Kd8" "65.Bd3" "Be3" "66.Qxf5" "Qc6" "67.Qf8+"
## [134] "Kc7" "68.Qe7+" "Kc8" "69.Bf5+" "Kb8" "70.Qd8+" "Kb7"
## [141] "71.Qd7+" "Qxd7" "72.Bxd7" "Kc7" "73.Bb5"
##
## [[3]]
## [1] "1.e4" "c5" "2.c3" "d5" "3.exd5" "Qxd5" "4.d4"
## [8] "Nf6" "5.Nf3" "Bg4" "6.Be2" "e6" "7.0-0" "Nc6"
## [15] "8.Be3" "cxd4" "9.cxd4" "Bb4" "10.a3" "Ba5" "11.Nc3"
## [22] "Qd6" "12.Ne5" "Bxe2" "13.Qxe2" "Bxc3" "14.bxc3" "Nxe5"
## [29] "15.Bf4" "Nf3+" "16.Qxf3" "Qd5" "17.Qd3" "Rc8" "18.Rfc1"
## [36] "Qc4" "19.Qxc4" "Rxc4" "20.Rcb1" "b6" "21.Bb8" "Ra4"
## [43] "22.Rb4" "Ra5" "23.Rc4" "0-0" "24.Bd6" "Ra8" "25.Rc6"
## [50] "b5" "26.Kf1" "Ra4" "27.Rb1" "a6" "28.Ke2" "h5"
## [57] "29.Kd3" "Rd8" "30.Be7" "Rd7" "31.Bxf6" "gxf6" "32.Rb3"
## [64] "Kg7" "33.Ke3" "e5" "34.g3" "exd4+" "35.cxd4" "Re7+"
## [71] "36.Kf3" "Rd7" "37.Rd3" "Raxd4" "38.Rxd4" "Rxd4" "39.Rxa6"
## [78] "b4"
##
## [[4]]
## [1] "1.Nf3" "d5" "2.d4" "c6" "3.c4" "e6"
## [7] "4.Nbd2" "Nf6" "5.e3" "Nbd7" "6.Bd3" "Bd6"
## [13] "7.e4" "dxe4" "8.Nxe4" "Nxe4" "9.Bxe4" "0-0"
## [19] "10.0-0" "h6" "11.Bc2" "e5" "12.Re1" "exd4"
## [25] "13.Qxd4" "Bc5" "14.Qc3" "a5" "15.a3" "Nf6"
## [31] "16.Be3" "Bxe3" "17.Rxe3" "Bg4" "18.Ne5" "Re8"
## [37] "19.Rae1" "Be6" "20.f4" "Qc8" "21.h3" "b5"
## [43] "22.f5" "Bxc4" "23.Nxc4" "bxc4" "24.Rxe8+" "Nxe8"
## [49] "25.Re4" "Nf6" "26.Rxc4" "Nd5" "27.Qe5" "Qd7"
## [55] "28.Rg4" "f6" "29.Qd4" "Kh7" "30.Re4" "Rd8"
## [61] "31.Kh1" "Qc7" "32.Qf2" "Qb8" "33.Ba4" "c5"
## [67] "34.Bc6" "c4" "35.Rxc4" "Nb4" "36.Bf3" "Nd3"
## [73] "37.Qh4" "Qxb2" "38.Qg3" "Qxa3" "39.Rc7" "Qf8"
## [79] "40.Ra7" "Ne5" "41.Rxa5" "Qf7" "42.Rxe5" "fxe5"
## [85] "43.Qxe5" "Re8" "44.Qf4" "Qf6" "45.Bh5" "Rf8"
## [91] "46.Bg6+" "Kh8" "47.Qc7" "Qd4" "48.Kh2" "Ra8"
## [97] "49.Bh5" "Qf6" "50.Bg6" "Rg8"
##
## [[5]]
## [1] "1.e4" "e5" "2.Nf3" "Nf6" "3.Nc3" "Nc6"
## [7] "4.d4" "exd4" "5.Nxd4" "Bb4" "6.Nxc6" "bxc6"
## [13] "7.Bd3" "d5" "8.exd5" "cxd5" "9.0-0" "0-0"
## [19] "10.Bg5" "c6" "11.Qf3" "Be7" "12.Rae1" "Re8"
## [25] "13.Ne2" "h6" "14.Bf4" "Bd6" "15.Nd4" "Bg4"
## [31] "16.Qg3" "Bxf4" "17.Qxf4" "Qb6" "18.c4" "Bd7"
## [37] "19.cxd5" "cxd5" "20.Rxe8+" "Rxe8" "21.Qd2" "Ne4"
## [43] "22.Bxe4" "dxe4" "23.b3" "Rd8" "24.Qc3" "f5"
## [49] "25.Rd1" "Be6" "26.Qe3" "Bf7" "27.Qc3" "f4"
## [55] "28.Rd2" "Qf6" "29.g3" "Rd5" "30.a3" "Kh7"
## [61] "31.Kg2" "Qe5" "32.f3" "e3" "33.Rd3" "e2"
## [67] "34.gxf4" "e1=Q" "35.fxe5" "Qxc3" "36.Rxc3" "Rxd4"
## [73] "37.b4" "Bc4" "38.Kf2" "g5" "39.Re3" "Be6"
## [79] "40.Rc3" "Bc4" "41.Re3" "Rd2+" "42.Ke1" "Rd3"
## [85] "43.Kf2" "Kg6" "44.Rxd3" "Bxd3" "45.Ke3" "Bc2"
## [91] "46.Kd4" "Kf5" "47.Kd5" "h5"
##
## [[6]]
## [1] "1.Nf3" "d5" "2.d4" "c6" "3.c4" "e6" "4.Nbd2"
## [8] "Nf6" "5.e3" "c5" "6.b3" "Nc6" "7.Bb2" "cxd4"
## [15] "8.exd4" "Be7" "9.Rc1" "0-0" "10.Bd3" "Bd7" "11.0-0"
## [22] "Nh5" "12.Re1" "Nf4" "13.Bb1" "Bd6" "14.g3" "Ng6"
## [29] "15.Ne5" "Rc8" "16.Nxd7" "Qxd7" "17.Nf3" "Bb4" "18.Re3"
## [36] "Rfd8" "19.h4" "Nge7" "20.a3" "Ba5" "21.b4" "Bc7"
## [43] "22.c5" "Re8" "23.Qd3" "g6" "24.Re2" "Nf5" "25.Bc3"
## [50] "h5" "26.b5" "Nce7" "27.Bd2" "Kg7" "28.a4" "Ra8"
## [57] "29.a5" "a6" "30.b6" "Bb8" "31.Bc2" "Nc6" "32.Ba4"
## [64] "Re7" "33.Bc3" "Ne5" "34.dxe5" "Qxa4" "35.Nd4" "Nxd4"
## [71] "36.Qxd4" "Qd7" "37.Bd2" "Re8" "38.Bg5" "Rc8" "39.Bf6+"
## [78] "Kh7" "40.c6" "bxc6" "41.Qc5" "Kh6" "42.Rb2" "Qb7"
## [85] "43.Rb4"
##
## [[7]]
## [1] "1.Nf3" "d5" "2.g3" "Bg4" "3.b3" "Nd7"
## [7] "4.Bb2" "e6" "5.Bg2" "Ngf6" "6.0-0" "c6"
## [13] "7.d3" "Bd6" "8.Nbd2" "0-0" "9.h3" "Bh5"
## [19] "10.e3" "h6" "11.Qe1" "Qa5" "12.a3" "Bc7"
## [25] "13.Nh4" "g5" "14.Nhf3" "e5" "15.e4" "Rfe8"
## [31] "16.Nh2" "Qb6" "17.Qc1" "a5" "18.Re1" "Bd6"
## [37] "19.Ndf1" "dxe4" "20.dxe4" "Bc5" "21.Ne3" "Rad8"
## [43] "22.Nhf1" "g4" "23.hxg4" "Nxg4" "24.f3" "Nxe3"
## [49] "25.Nxe3" "Be7" "26.Kh1" "Bg5" "27.Re2" "a4"
## [55] "28.b4" "f5" "29.exf5" "e4" "30.f4" "Bxe2"
## [61] "31.fxg5" "Ne5" "32.g6" "Bf3" "33.Bc3" "Qb5"
## [67] "34.Qf1" "Qxf1+" "35.Rxf1" "h5" "36.Kg1" "Kf8"
## [73] "37.Bh3" "b5" "38.Kf2" "Kg7" "39.g4" "Kh6"
## [79] "40.Rg1" "hxg4" "41.Bxg4" "Bxg4" "42.Nxg4+" "Nxg4+"
## [85] "43.Rxg4" "Rd5" "44.f6" "Rd1" "45.g7"
##
## [[8]]
## [1] "1.e4" "e5" "2.Nf3" "Nc6" "3.Bb5" "a6" "4.Ba4"
## [8] "Nf6" "5.0-0" "Be7" "6.Re1" "b5" "7.Bb3" "d6"
## [15] "8.c3" "0-0" "9.h3" "h6" "10.d4" "Re8" "11.Nbd2"
## [22] "Bf8" "12.Nf1" "Bd7" "13.Ng3" "Na5" "14.Bc2" "c5"
## [29] "15.b3" "Nc6" "16.d5" "Ne7" "17.Be3" "Ng6" "18.Qd2"
## [36] "Nh7" "19.a4" "Nh4" "20.Nxh4" "Qxh4" "21.Qe2" "Qd8"
## [43] "22.b4" "Qc7" "23.Rec1" "c4" "24.Ra3" "Rec8" "25.Rca1"
## [50] "Qd8" "26.f4" "Nf6" "27.fxe5" "dxe5" "28.Qf1" "Ne8"
## [57] "29.Qf2" "Nd6" "30.Bb6" "Qe8" "31.R3a2" "Be7" "32.Bc5"
## [64] "Bf8" "33.Nf5" "Bxf5" "34.exf5" "f6" "35.Bxd6" "Bxd6"
## [71] "36.axb5" "axb5" "37.Be4" "Rxa2" "38.Qxa2" "Qd7" "39.Qa7"
## [78] "Rc7" "40.Qb6" "Rb7" "41.Ra8+" "Kf7" "42.Qa6" "Qc7"
## [85] "43.Qc6" "Qb6+" "44.Kf1" "Rb8" "45.Ra6"
##
## [[9]]
## [1] "1.d3" "e5" "2.Nf3" "Nc6" "3.c4" "Nf6"
## [7] "4.a3" "d6" "5.Nc3" "Be7" "6.g3" "0-0"
## [13] "7.Bg2" "Be6" "8.0-0" "Qd7" "9.Ng5" "Bf5"
## [19] "10.e4" "Bg4" "11.f3" "Bh5" "12.Nh3" "Nd4"
## [25] "13.Nf2" "h6" "14.Be3" "c5" "15.b4" "b6"
## [31] "16.Rb1" "Kh8" "17.Rb2" "a6" "18.bxc5" "bxc5"
## [37] "19.Bh3" "Qc7" "20.Bg4" "Bg6" "21.f4" "exf4"
## [43] "22.gxf4" "Qa5" "23.Bd2" "Qxa3" "24.Ra2" "Qb3"
## [49] "25.f5" "Qxd1" "26.Bxd1" "Bh7" "27.Nh3" "Rfb8"
## [55] "28.Nf4" "Bd8" "29.Nfd5" "Nc6" "30.Bf4" "Ne5"
## [61] "31.Ba4" "Nxd5" "32.Nxd5" "a5" "33.Bb5" "Ra7"
## [67] "34.Kg2" "g5" "35.Bxe5+" "dxe5" "36.f6" "Bg6"
## [73] "37.h4" "gxh4" "38.Kh3" "Kg8" "39.Kxh4" "Kh7"
## [79] "40.Kg4" "Bc7" "41.Nxc7" "Rxc7" "42.Rxa5" "Rd8"
## [85] "43.Rf3" "Kh8" "44.Kh4" "Kg8" "45.Ra3" "Kh8"
## [91] "46.Ra6" "Kh7" "47.Ra3" "Kh8" "48.Ra6"
##
## [[10]]
## [1] "1.e4" "c6" "2.d4" "d6" "3.Nf3" "Nf6"
## [7] "4.Nc3" "Bg4" "5.h3" "Bh5" "6.Bd3" "e6"
## [13] "7.Qe2" "d5" "8.Bg5" "Be7" "9.e5" "Nfd7"
## [19] "10.Bxe7" "Qxe7" "11.g4" "Bg6" "12.Bxg6" "hxg6"
## [25] "13.h4" "Na6" "14.0-0-0" "0-0-0" "15.Rdg1" "Nc7"
## [31] "16.Kb1" "f6" "17.exf6" "Qxf6" "18.Rg3" "Rde8"
## [37] "19.Re1" "Rhf8" "20.Nd1" "e5" "21.dxe5" "Qf4"
## [43] "22.a3" "Ne6" "23.Nc3" "Ndc5" "24.b4" "Nd7"
## [49] "25.Qd3" "Qf7" "26.b5" "Ndc5" "27.Qe3" "Qf4"
## [55] "28.bxc6" "bxc6" "29.Rd1" "Kc7" "30.Ka1" "Qxe3"
## [61] "31.fxe3" "Rf7" "32.Rh3" "Ref8" "33.Nd4" "Rf2"
## [67] "34.Rb1" "Rg2" "35.Nce2" "Rxg4" "36.Nxe6+" "Nxe6"
## [73] "37.Nd4" "Nxd4" "38.exd4" "Rxd4" "39.Rg1" "Rc4"
## [79] "40.Rxg6" "Rxc2" "41.Rxg7+" "Kb6" "42.Rb3+" "Kc5"
## [85] "43.Rxa7" "Rf1+" "44.Rb1" "Rff2" "45.Rb4" "Rc1+"
## [91] "46.Rb1" "Rcc2" "47.Rb4" "Rc1+" "48.Rb1" "Rxb1+"
## [97] "49.Kxb1" "Re2" "50.Re7" "Rh2" "51.Rh7" "Kc4"
## [103] "52.Rc7" "c5" "53.e6" "Rxh4" "54.e7" "Re4"
## [109] "55.a4" "Kb3" "56.Kc1"
##
## [[11]]
## [1] "1.Nf3" "d5" "2.g3" "Bg4" "3.Bg2" "Nd7"
## [7] "4.h3" "Bxf3" "5.Bxf3" "c6" "6.d3" "e6"
## [13] "7.e4" "Ne5" "8.Bg2" "dxe4" "9.Bxe4" "Nf6"
## [19] "10.Bg2" "Bb4+" "11.Nd2" "h5" "12.Qe2" "Qc7"
## [25] "13.c3" "Be7" "14.d4" "Ng6" "15.h4" "e5"
## [31] "16.Nf3" "exd4" "17.Nxd4" "0-0-0" "18.Bg5" "Ng4"
## [37] "19.0-0-0" "Rhe8" "20.Qc2" "Kb8" "21.Kb1" "Bxg5"
## [43] "22.hxg5" "N6e5" "23.Rhe1" "c5" "24.Nf3" "Rxd1+"
## [49] "25.Rxd1" "Nc4" "26.Qa4" "Rd8" "27.Re1" "Nb6"
## [55] "28.Qc2" "Qd6" "29.c4" "Qg6" "30.Qxg6" "fxg6"
## [61] "31.b3" "Nxf2" "32.Re6" "Kc7" "33.Rxg6" "Rd7"
## [67] "34.Nh4" "Nc8" "35.Bd5" "Nd6" "36.Re6" "Nb5"
## [73] "37.cxb5" "Rxd5" "38.Rg6" "Rd7" "39.Nf5" "Ne4"
## [79] "40.Nxg7" "Rd1+" "41.Kc2" "Rd2+" "42.Kc1" "Rxa2"
## [85] "43.Nxh5" "Nd2" "44.Nf4" "Nxb3+" "45.Kb1" "Rd2"
## [91] "46.Re6" "c4" "47.Re3" "Kb6" "48.g6" "Kxb5"
## [97] "49.g7" "Kb4"
##
## [[12]]
## [1] "1.e4" "c6" "2.d4" "d5" "3.Nc3" "dxe4" "4.Nxe4"
## [8] "Nd7" "5.Ng5" "Ngf6" "6.Bd3" "e6" "7.N1f3" "h6"
## [15] "8.Nxe6" "Qe7" "9.0-0" "fxe6" "10.Bg6+" "Kd8" "11.Bf4"
## [22] "b5" "12.a4" "Bb7" "13.Re1" "Nd5" "14.Bg3" "Kc8"
## [29] "15.axb5" "cxb5" "16.Qd3" "Bc6" "17.Bf5" "exf5" "18.Rxe7"
## [36] "Bxe7" "19.c4"