Skip to content

Instantly share code, notes, and snippets.

@IISResetMe
Last active April 9, 2017 12:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IISResetMe/1cb193f9473906d03277 to your computer and use it in GitHub Desktop.
Save IISResetMe/1cb193f9473906d03277 to your computer and use it in GitHub Desktop.
Parallel PGN result parser with minimal error handling
param(
[parameter(Position=0)]
$ChessFolder = 'D:\iisresetme\ChessData\'
)
# Prepare C# method to read and process the files
$Challenge = @{
Name = 'ResultCounter'
Namespace = 'ChessData'
PassThru = $true
UsingNamespace = @(
'System.Collections',
'System.Collections.Concurrent',
'System.Collections.Generic',
'System.IO',
'System.Threading.Tasks'
)
MemberDefinition = @'
public static Hashtable CountChessResults(string folder)
{
// Use a ConcurrentDictionary to avoid concurrent updates from Parallel.ForEach() overwriting each other
ConcurrentDictionary<string, int> winners = new ConcurrentDictionary<string, int>(new Dictionary<string, int>() { { "Draw", 0 }, { "Black", 0 }, { "White", 0 } });
Parallel.ForEach<string>(Directory.EnumerateFiles(folder, "*.pgn", SearchOption.AllDirectories), fileName =>
{
// StreamReader.ReadLine() seems to be the fastest text reader
using (StreamReader reader = new StreamReader(fileName))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith("[Result "))
{
if(line.Contains("1/2"))
winners.AddOrUpdate("Draw", 1, (k, v) => v + 1);
else if(line.Contains("1-0"))
winners.AddOrUpdate("Black", 1, (k, v) => v + 1);
else if(line.Contains("0-1"))
winners.AddOrUpdate("White", 1, (k, v) => v + 1);
}
}
}
});
// return a hashtable, easily converted to an object in PowerShell
return new Hashtable(winners);
}
'@
}
$CompilerParams = [System.CodeDom.Compiler.CompilerParameters]::new()
$CompilerParams.CompilerOptions = "/optimize+ /warn:0"
# Don't try to re-add assembly if it already exists (ie. someone already ran the script once before)
try {
$ChallengeModule = [ChessData.ResultCounter] -as [type]
}
catch{
$ChallengeModule = Add-Type @Challenge -CompilerParameters $CompilerParams |Select-Object -First 1
}
# wrapper function that takes a path to the root folder containing *.pgn files
function Measure-PGNResult
{
param($path)
return New-Object psobject -Property $ChallengeModule::CountChessResults($(Resolve-Path $path).Path)
}
Measure-Command {
$Results = Measure-PGNResult $ChessFolder
}
$Results
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment