Advertisement
Guest User

Untitled

a guest
Jan 28th, 2024
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 3.99 KB | Source Code | 0 0
  1. using System.Buffers;
  2. using System.Collections.Immutable;
  3. using System.IO.Pipelines; // add this package with "dotnet add package System.IO.Pipelines"
  4. using System.Text;
  5.  
  6. namespace ProcessChessData;
  7.  
  8. public class Program
  9. {
  10.     // file list generated using this PowerShell command:
  11.     //   git clone https://github.com/rozim/ChessData.git ../ChessData
  12.     //   gci ../ChessData/*.pgn -recurse | foreach { $_.fullname } | where { (file $_).EndsWith('ASCII text') } > file_list.txt
  13.     private const string FILE_LIST_FILENAME = @"file_list.txt";
  14.  
  15.     private readonly static ImmutableArray<byte> RESULT_PREFIX = ImmutableArray.Create(Encoding.ASCII.GetBytes("[Result \""));
  16.  
  17.     public static async Task Main(string[] args)
  18.     {
  19.         var fileNames = await File.ReadAllLinesAsync(FILE_LIST_FILENAME);
  20.         var results   = await Task.WhenAll(fileNames.Select(ProcessFile));
  21.         var total     = results.Aggregate((BlackWins: 0, WhiteWins: 0, Draws: 0), (tot, next) => (tot.BlackWins + next.BlackWins, tot.WhiteWins + next.WhiteWins, tot.Draws + next.Draws));
  22.  
  23.         Console.WriteLine($"{total.BlackWins} - {total.WhiteWins} - {total.Draws}");
  24.     }
  25.  
  26.     private static async Task<(int BlackWins, int WhiteWins, int Draws)> ProcessFile(string fileName)
  27.     {
  28.         var blackWins = 0;
  29.         var whiteWins = 0;
  30.         var draws     = 0;
  31.  
  32.         try {
  33.             await using var fs = File.OpenRead(fileName);
  34.  
  35.             var reader = PipeReader.Create(fs);
  36.  
  37.             while (true) {
  38.                 var readResult = await reader.ReadAsync();
  39.  
  40.                 // parse result lines out of readResult.Buffer
  41.                 var lastProcessedEndPosition = ParseResultLines(readResult.Buffer, ref blackWins, ref whiteWins, ref draws);
  42.  
  43.                 // advance to the end of what we actually parsed, and let the pipeline know that
  44.                 // we examined the entire buffer (so it'll actually get more data from disk next time)
  45.                 reader.AdvanceTo(lastProcessedEndPosition, readResult.Buffer.End);
  46.  
  47.                 // break out when we're done
  48.                 if (readResult.IsCompleted) {
  49.                     break;
  50.                 }
  51.             }
  52.  
  53.             await reader.CompleteAsync();
  54.  
  55.             return (blackWins, whiteWins, draws);
  56.         } catch (InvalidDataException ide) {
  57.             throw new InvalidOperationException($"File processing failed for file {fileName}", ide);
  58.         }
  59.     }
  60.  
  61.     private static SequencePosition ParseResultLines(ReadOnlySequence<byte> buffer, ref int blackWins, ref int whiteWins, ref int draws)
  62.     {
  63.         var prefixSpan     = RESULT_PREFIX.AsSpan();
  64.         var sequenceReader = new SequenceReader<byte>(buffer);
  65.  
  66.         while (!sequenceReader.End) {
  67.             // ok, so here, we're at the beginning of a line
  68.  
  69.             // if we're not on a result line, then we want to skip it
  70.             if (!sequenceReader.IsNext(prefixSpan)) {
  71.                 if (!sequenceReader.TryAdvanceTo((byte)'\n', true)) {
  72.                     // we don't have up to the next line; we're done here
  73.                     break;
  74.                 }
  75.  
  76.                 // otherwise, we advanced to the next line, so we can start over up top
  77.                 continue;
  78.             }
  79.  
  80.             // if we're here, then we're on a result line, so try to read out the
  81.             // complete line; if we can't do that, we can bail for now
  82.             if (!sequenceReader.TryReadTo(out ReadOnlySpan<byte> lineSpan, (byte)'\n', true)) {
  83.                 break;
  84.             }
  85.  
  86.             // alright; we have the result line in the lineSpan variable; parse it out
  87.             var dashPos = lineSpan.IndexOf((byte)'-');
  88.  
  89.             if (dashPos == -1) {
  90.                 // if the result is *, that means "game still in progress, game abandoned, or result otherwise unknown"
  91.                 // therefore, we can't add any results for that game
  92.                 if (lineSpan[9] == (byte)'*') {
  93.                     continue;
  94.                 }
  95.  
  96.                 throw new InvalidDataException($"No dash found in result: {Encoding.ASCII.GetString(lineSpan)}");
  97.             }
  98.  
  99.             switch (lineSpan[dashPos - 1]) {
  100.                 case (byte)'0': blackWins++; break; // black win
  101.                 case (byte)'1': whiteWins++; break; // white win
  102.                 case (byte)'2': draws++;     break; // draw
  103.                 default: throw new InvalidDataException($"Invalid character ({Encoding.ASCII.GetString(new[] { lineSpan[dashPos - 1] })} in result line data: {Encoding.ASCII.GetString(lineSpan)}");
  104.             }
  105.         }
  106.  
  107.         return sequenceReader.Position;
  108.     }
  109. }
  110.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement