Rob Herbison
Northeastern University
November 29, 2017
Documentation and all code for this project is found at http://rpubs.com/rherbison/markovchess
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+"
R is used to read the text files and create the nested vectors holding the moves.
The normal convention for numbering chess games is replaced, for computational purposes, by a count for each piece moved. 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.
Vectors are created to determine what characteristics are happening at each entry, including whether the entry is in a particular state. These form truth tables of the same dimensions as the chess data entries.
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)
}
#Note that only oCapture, oCheck, Both, and Else correspond to the state space.
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)
## [1] 0.011 0.179 0.032 0.013 0.766
For the purpose of creating a transition matrix a count vector is defined. This gives 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:
## [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)
The transition matrix is generated by iterating through the truth tables to determine the historical probabilities of jumps between states. The count vector is compared with the jumps to form a row stochastic matrix. 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
round(T,3)
## Initial oCapture oCheck Both Else
## Initial 0 0.000 0.000 0.000 1.000
## oCapture 0 0.449 0.020 0.030 0.500
## oCheck 0 0.057 0.000 0.000 0.943
## Both 0 0.692 0.000 0.077 0.231
## Else 0 0.117 0.037 0.008 0.837
To find the stationary vector the first eigenvector of the transpose of the transition matrix was taken and normalized.
Stationary Distribution:
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)
round(StationaryVector, 3)
## Initial oCapture oCheck Both Else
## [1,] 0 0.184 0.032 0.013 0.771
Here is an output for a test of the stationary vector. It is multiplied 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.
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.
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", verbose = FALSE, quiet = TRUE)
library(igraph, quietly = TRUE, warn.conflicts = FALSE)
## Warning: package 'igraph' was built under R version 3.3.3
TGraph <- graph_from_adjacency_matrix(T, weighted=TRUE)
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.
Consider another model on the state space corresponding to the null hypothesis. In this model, the probability of being in a state is based only the empirical vector. There are three substantial deviations. Compare the transition matrix with the empirical vector:
## Initial oCapture oCheck Both Else
## Initial 0 0.000 0.000 0.000 1.000
## oCapture 0 0.449 0.020 0.030 0.500
## oCheck 0 0.057 0.000 0.000 0.943
## Both 0 0.692 0.000 0.077 0.231
## Else 0 0.117 0.037 0.008 0.837
## [1] 0.01084991 0.17902351 0.03164557 0.01265823 0.76582278
There are two things to point out. Captures, including “Both” are very likely to be followed by captures. And “oCheck” is almost always followed by “Else”. The probability of a capture following “Both” in the null hypothesis is 0.17902351. The transition matrix puts it at 0.69230769. Further study with a larger sample size is necessary to determine if these differences are significant.
It seems likely that checks are followed by moves that neither take or check because of the black-white turn cycle. Checks may clump strongly if the experiment is run on every other move, to capture either white or black moves. This does seem to show that when a player is placed in check they are not frequently able to respond with check or a capture. These tests have the lowest sample size so they should be rerun on a larger data set.
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 to the questions of how captures and checks clump. Additionally, the methods available to analyze chess are limited because of the discrete nature of the game. The Markov model is an excellent way of analyzing chess.
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 data set as nested vectors showing all twelve games. 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"
Here is a collection of the captures and the else state:
## [1] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
## [12] FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE
## [23] FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE
## [34] FALSE TRUE TRUE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE
## [45] FALSE TRUE TRUE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
## [56] FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [67] FALSE TRUE TRUE FALSE FALSE FALSE TRUE
## [1] TRUE TRUE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
## [12] TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE TRUE TRUE TRUE
## [23] TRUE TRUE TRUE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE
## [34] TRUE FALSE FALSE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE
## [45] TRUE FALSE FALSE FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE
## [56] TRUE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE
## [67] TRUE FALSE FALSE TRUE FALSE TRUE FALSE