#!/usr/bin/awk -f # # Copyright (c) 2015, 2016 Matthias Kilian # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. BEGIN { SUBSEP = "\t" # Some game logs have the starting message after the first # move(s) due to race conditions: START_FNR[92560] = 2 START_FNR[92594] = 2 START_FNR[92983] = 2 START_FNR[93095] = 3 START_FNR[93145] = 2 START_FNR[94084] = 4 START_FNR[94087] = 2 START_FNR[94416] = 2 START_FNR[96085] = 2 START_FNR[96092] = 2 START_FNR[97905] = 4 START_FNR[99479] = 2 START_FNR[99513] = 2 START_FNR[99530] = 61406 # totally broken START_FNR[99538] = 2 START_FNR[101431] = 2 START_FNR[101435] = 4 START_FNR[101436] = 2 START_FNR[101437] = 2 START_FNR[101438] = 2 START_FNR[101443] = 2 START_FNR[101454] = 2 START_FNR[101463] = 2 START_FNR[101588] = 2 START_FNR[101607] = 2 START_FNR[101609] = 2 START_FNR[101641] = 2 START_FNR[101645] = 2 START_FNR[101646] = 30653 START_FNR[101647] = 2 START_FNR[101654] = 9799 START_FNR[101655] = 2 START_FNR[101656] = 3 START_FNR[101663] = 2 START_FNR[101832] = 2 START_FNR[102648] = 2 } # Try to get the user name starting at $3 and ending in $n function grabuser(n, i, u) { u = $(i = 3) while (++i <= n) u = u " " $i this_users[u] = 1 return u } # Accumulate games per user and reset current game tracking arrays. function update_game_counts(u) { if (!game_over) { # If everyone is done (finished or booted), this game # must be over. game_over = 1 for (u in this_users) if (!(u in done_users)) { game_over = 0 break } } if (!game_over) for (u in this_users) for (v in this_users) if (u < v) COMMON_GAMES[u, v]++ game_over = 0 delete this_users delete done_users } # We need the base name of the log file (i.e. the game ID) to skip # very old log files and to fix some minor errors in a few newer # log files. Also, update game_counts from the previous logfile (if # any). FNR == 1 { update_game_counts() GID = FILENAME sub(/.*\//, "", GID) sub(/\.log$/, "", GID) GID += 0 # First game with valid log entries: 1674... if (GID < 1674) nextfile } # This is set for game 2324 in the next block: next_line { saved = $0 $0 = next_line next_line = 0 u = grabuser(NF - 3) MOVES[u]++ if (next_re) { RE[u]++ next_re = 0 } $0 = saved } # Fix some partially corrupted log files. { # ... first 19 entries don't have a timestamp: if (GID == 1674 && FNR < 20) next # Game 2768 has a wrong year in the date: else if (GID == 2768 && FNR == 219) sub(/^22005/, 2005) # Garbled lines. Simulate new round log, restore the move # log, ... else if (GID == 2940 && FNR == 68 || GID == 2860 && FNR == 141) { sub(/.*-------*/, "") # ... and ignore the misplaced new round log. } else if (GID == 2940 && FNR == 69 || GID == 2860 && FNR == 142) next # Incomplete last line in game 3434: else if (GID == 3434 && FNR == 68) next # Spurious space in vector: else if (GID == 5415 && FNR == 119) { $(NF - 1) = $(NF - 1) $NF NF-- } # Truncated line merged with next line: else if (GID == 2324 && FNR == 354) { next_line = $0 sub(/.*Vekto2005/, "2005", next_line) sub(/Vekto2005.*/, "fake") } # Game start comes after the first or even second move: else if (FNR == START_FNR[GID]) next } # Skip empty lines /^[[:space:]]*$/ { next } # Error out on lines without a proper timestamp !/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]:? / { printf("%s:%d: unrecognized input: %s\n", FILENAME, FNR, $0) \ > "/dev/stderr" next } # Starting line FNR == 1 && $3 == "Spiel" && $NF == "erstellt" { next } # Overlong starting line, split up into several lines FNR == 1 && $3 == "Spiel" && $NF != "erstellt" { while(getline > 0 && $NF != "erstellt") ; if($NF == "erstellt") next printf("%s:%d: unrecognized input: %s\n", FILENAME, FNR, $0) \ > "/dev/stderr" next } NF == 3 && $3 == "-----------------------------------" { next } NF < 4 { printf("%s:%d: unrecognized input: %s\n", FILENAME, FNR, $0) \ > "/dev/stderr" nextfile } # Normal move $(NF - 2) == "->" { u = grabuser(NF - 3) next } # Crash (with ZZZ:) $(NF - 1) == "CRASHT!!!" && $(NF) ~ /^ZZZ:/ { u = grabuser(NF - 2) next } # Crash (without ZZZ:) $NF == "CRASHT!!!" { u = grabuser(NF - 1) next } # Quit / steigt aus dem Spiel aus$/ { u = grabuser(NF - 5) # Don't count. delete this_users[u] next } # Parc femme / wird [0-9][0-9]*\.$/ { u = grabuser(NF - 2) done_users[u] = 1 next } # Booted / wird von .* aus dem Spiel geworfen$/ { # Eek! There's a user (Jones Villeneuve) with white space in the # name who was allowed to kick other users, probably also with # white space in the name. sub(/ wird von .* aus dem Spiel geworfen$/, "") u = grabuser(NF) done_users[u] = 1 next } # Game aborted / hat das Spiel auf beendet gesetzt$/ { game_over = 1 next } # Game finished / Spiel beendet$/ { game_over = 1 next } { printf("%s:%d: unrecognized input: %s\n", FILENAME, FNR, $0) \ > "/dev/stderr" nextfile } END { update_game_counts() for (uv in COMMON_GAMES) printf("%s\t%d\n", uv, COMMON_GAMES[uv]) }