1. Introduction

This project converts a semi-structured chess tournament text file into a tidy CSV dataset suitable for import into a relational database. Each player’s name, state, tournament points, pre-tournament rating, and average pre-tournament rating of opponents are extracted from the original records.

2. Data source and preparation

The input is tournamentinfo.txt, supplied with the assignment. Place it in the same folder as this R Markdown file before knitting. The tournament contains two lines per player: the first holds the player number, name, points, and seven round results; the second holds state, USCF ID, and pre/post rating. Separator lines are ignored.

# This project uses only base R; no package installation is required.
options(stringsAsFactors = FALSE)
input_file <- "tournamentinfo.txt"
if (!file.exists(input_file)) {
  stop("Place tournamentinfo.txt in the same folder as the Rmd file.")
}
lines <- readLines(input_file, warn = FALSE)
cat("Number of text lines:", length(lines), "\n")
## Number of text lines: 196
head(lines, 8)
## [1] "-----------------------------------------------------------------------------------------" 
## [2] " Pair | Player Name                     |Total|Round|Round|Round|Round|Round|Round|Round| "
## [3] " Num  | USCF ID / Rtg (Pre->Post)       | Pts |  1  |  2  |  3  |  4  |  5  |  6  |  7  | "
## [4] "-----------------------------------------------------------------------------------------" 
## [5] "    1 | GARY HUA                        |6.0  |W  39|W  21|W  18|W  14|W   7|D  12|D   4|" 
## [6] "   ON | 15445895 / R: 1794   ->1817     |N:2  |W    |B    |W    |B    |W    |B    |W    |" 
## [7] "-----------------------------------------------------------------------------------------" 
## [8] "    2 | DAKSHESH DARURI                 |6.0  |W  63|W  58|L   4|W  17|W  16|W  20|W   7|"

3. Extract player records

Split each row at the vertical bars. A player row begins with a numeric player number and is immediately followed by a state/rating row. The first integer after R: is the pre-tournament rating, including provisional ratings such as 1641P17.

split_fields <- function(line) trimws(strsplit(line, "|", fixed = TRUE)[[1]])

records <- list()
for (i in seq_len(length(lines) - 1L)) {
  first <- split_fields(lines[i])
  second <- split_fields(lines[i + 1L])
  if (length(first) < 10L || !grepl("^[0-9]+$", first[1])) next
  if (length(second) < 2L || !grepl("^[A-Z]{2}$", second[1])) next

  rating_match <- regexec("R:[[:space:]]*([0-9]+)", second[2])
  rating_parts <- regmatches(second[2], rating_match)[[1]]
  if (length(rating_parts) < 2L) stop("Missing pre-rating for player ", first[1])

  round_cells <- first[4:10]
  # Only W/L/D followed by a player number represents a played opponent.
  opponent_ids <- vapply(round_cells, function(cell) {
    m <- regexec("^[WLD][[:space:]]+([0-9]+)$", cell)
    parts <- regmatches(cell, m)[[1]]
    if (length(parts) == 2L) as.integer(parts[2]) else NA_integer_
  }, integer(1))

  records[[length(records) + 1L]] <- list(
    player_id = as.integer(first[1]),
    player_name = tools::toTitleCase(tolower(first[2])),
    state = second[1],
    points = as.numeric(first[3]),
    pre_rating = as.integer(rating_parts[2]),
    opponents = opponent_ids
  )
}

players <- data.frame(
  player_id = vapply(records, `[[`, integer(1), "player_id"),
  player_name = vapply(records, `[[`, character(1), "player_name"),
  state = vapply(records, `[[`, character(1), "state"),
  points = vapply(records, `[[`, numeric(1), "points"),
  pre_rating = vapply(records, `[[`, integer(1), "pre_rating")
)
cat("Number of players:", nrow(players), "\n")
## Number of players: 64
stopifnot(nrow(players) == 64L, !anyDuplicated(players$player_id))
head(players)
##   player_id         player_name state points pre_rating
## 1         1            Gary Hua    ON    6.0       1794
## 2         2     Dakshesh Daruri    MI    6.0       1553
## 3         3        Aditya Bajaj    MI    6.0       1384
## 4         4 Patrick h Schilling    MI    5.5       1716
## 5         5          Hanshi Zuo    MI    5.5       1655
## 6         6         Hansen Song    OH    5.0       1686

4. Match opponents and calculate average ratings

For each played round, the opponent number refers to the player number in the tournament. Byes (B), half-point byes (H), unplayed rounds (U), and forfeits (X) do not identify a played opponent and are excluded. The denominator is therefore the number of identifiable played opponents, not necessarily seven.

ratings_by_id <- setNames(players$pre_rating, as.character(players$player_id))

average_opponent_rating <- vapply(records, function(record) {
  ids <- record$opponents[!is.na(record$opponents)]
  if (length(ids) == 0L) return(NA_real_)
  ratings <- unname(ratings_by_id[as.character(ids)])
  if (anyNA(ratings)) stop("Opponent ID not found for ", record$player_name)
  round(mean(ratings), 0)
}, numeric(1))

players$average_opponent_pre_rating <- average_opponent_rating
players$games_played <- vapply(records, function(x) sum(!is.na(x$opponents)), integer(1))

5. Validate Gary Hua’s record

Gary Hua played opponents 39, 21, 18, 14, 7, 12, and 4. Their pre-ratings are 1436, 1563, 1600, 1610, 1649, 1663, and 1716.

gary <- players[players$player_id == 1L, ]
gary_opponents <- records[[which(players$player_id == 1L)]]$opponents
gary_ratings <- unname(ratings_by_id[as.character(gary_opponents)])
print(gary_ratings)
## [1] 1436 1563 1600 1610 1649 1663 1716
cat("Unrounded mean:", mean(gary_ratings), "\n")
## Unrounded mean: 1605.286
cat("Rounded mean:", round(mean(gary_ratings)), "\n")
## Rounded mean: 1605
stopifnot(
  gary$player_name == "Gary Hua",
  gary$state == "ON",
  gary$points == 6,
  gary$pre_rating == 1794,
  gary$average_opponent_pre_rating == 1605
)

6. Export the required CSV

The exported file has exactly five columns, in the order requested by the assignment.

final_data <- data.frame(
  "Player Name" = players$player_name,
  "Player State" = players$state,
  "Total Points" = players$points,
  "Pre-Rating" = players$pre_rating,
  "Average Opponent Pre-Rating" = players$average_opponent_pre_rating,
  check.names = FALSE
)

output_file <- "DATA607_Chess_Tournament_Results.csv"
write.csv(final_data, output_file, row.names = FALSE, na = "")
cat("Saved:", normalizePath(output_file), "\n")
## Saved: C:\Users\Reggie\OneDrive\Documentos\DATA607_Chess_Tournament_Results.csv
knitr::kable(head(final_data, 10), caption = "First 10 chess tournament records")
First 10 chess tournament records
Player Name Player State Total Points Pre-Rating Average Opponent Pre-Rating
Gary Hua ON 6.0 1794 1605
Dakshesh Daruri MI 6.0 1553 1469
Aditya Bajaj MI 6.0 1384 1564
Patrick h Schilling MI 5.5 1716 1574
Hanshi Zuo MI 5.5 1655 1501
Hansen Song OH 5.0 1686 1519
Gary Dee Swathell MI 5.0 1649 1372
Ezekiel Houghton MI 5.0 1641 1468
Stefano Lee ON 5.0 1411 1523
Anvit Rao MI 5.0 1365 1554

7. Quality checks and exploratory analysis

stopifnot(nrow(final_data) == 64L, ncol(final_data) == 5L)
stopifnot(!anyNA(final_data))
summary(players[, c("points", "pre_rating", "average_opponent_pre_rating", "games_played")])
##      points        pre_rating   average_opponent_pre_rating  games_played  
##  Min.   :1.000   Min.   : 377   Min.   :1107                Min.   :1.000  
##  1st Qu.:2.500   1st Qu.:1227   1st Qu.:1310                1st Qu.:6.000  
##  Median :3.500   Median :1407   Median :1382                Median :7.000  
##  Mean   :3.438   Mean   :1378   Mean   :1379                Mean   :6.375  
##  3rd Qu.:4.000   3rd Qu.:1583   3rd Qu.:1481                3rd Qu.:7.000  
##  Max.   :6.000   Max.   :1794   Max.   :1605                Max.   :7.000
table(players$state)
## 
## MI OH ON 
## 55  1  8
plot(players$pre_rating, players$average_opponent_pre_rating,
     pch = 19, col = "steelblue",
     xlab = "Player pre-tournament rating",
     ylab = "Average opponent pre-tournament rating",
     main = "Player Rating vs. Average Opponent Rating")
abline(0, 1, lty = 2, col = "gray50")

8. Conclusion

The semi-structured tournament report was converted into a five-column CSV for all 64 players. Opponent pre-ratings were linked through player numbers and averaged over identifiable games. The result for Gary Hua was validated against the assignment example (1605). The generated CSV can be imported into a SQL database or used for further statistical analysis.

9. Reproducibility and publication

Keep tournamentinfo.txt alongside this .Rmd file. Click Knit in RStudio to generate the HTML report and CSV. To publish the report, choose Publish in the HTML viewer and select RPubs. Make the source text file accessible to anyone who needs to rerun the analysis (for example, in the same public project repository). RPubs publishes the report, not the input file or CSV automatically.