Reputation: 13
I am attempting to make a file parser that will take a raw input file and then create a new file that has all the elements in the correct order and with proper new lines so another parser can read it.
So my file is a PGN (Portable Games Notation). These files are used in chess to record games people play on the computer.
They look like:
--------------------------------------
[Event "Computer chess game"]
[Date "2015.10.28"]
[Round "?"]
[White "White Player"]
[Black "Black Player"]
[Result "1-0"]
[BlackElo "2400"]
[ECO "A25"]
[Opening "English"]
[Time "10:39:20"]
[Variation "Closed"]
[WhiteElo "2400"]
[Termination "normal"]
[PlyCount "63"]
[WhiteType "human"]
[BlackType "human"]
1. f3 e6 2. g4 Qh4# 1-0
-------------------------------
On Reddit's /r/chess, they have it to where you can surround your games with [pgn][/pgn]
and it will then create a playable board that you or others can step through your game and offer you advice, etc.
The problem is the site I play on, the PGN comes out as above. The /r/chess parser doesn't like it.
It prefers each move to be on a seperate line:
1. f3 e6
2. g4 Qh4# 1-0
Since I am trying to learn Powershell, I wanted to create a script that would open the raw PGN, and then reformat it to look like the above and maybe pull out Event, Date, White Player, Black Player, and the Result. Then format it so that after each move a new line is inserted. Then output a new file surrounded with [pgn][/pgn]
.
I am kind of lost on how to do this. Do I need to use a regular expression? I know that once I read a file into Powershell I can treat it like an array too.
The output file should look like this:
[pgn][Event "Computer chess game"]
[Date "2015.10.28"]
[White "White Player"]
[Black "Black Player"]
[Result "1-0"]
1. f3 e6
2. g4 Qh4# 1-0 [/pgn]
Any and all help is appreciated!
Upvotes: 0
Views: 151
Reputation: 6920
Here is the function, that will convert your PGNs to Reddit-compatible format. Please note, that currently it doesn't support PGNs with multiple games per file.
With this function, you can:
Event
, Date
, White
, Black
, and the Result
Function accepts following arguments:
Usage examples:
Convert file, output to screen
ConvertPgn-ForReddit -Path .\Foo.pgn
[pgn]
[Event "Computer chess game"]
[Date "2015.10.28"]
[White "White Player"]
[Black "Black Player"]
[Result "1-0"]
1. f3 e6
2. g4 Qh4# 1-0
[/pgn]
Convert file, output to file
ConvertPgn-ForReddit -Path .\Foo.pgn -OutFile .\Bar.pgn
Convert file, output to screen, keep only BlackElo
and Time
headers
ConvertPgn-ForReddit -Path .\Foo.pgn -KeepHeaders BlackElo, Time
[pgn]
[BlackElo "2400"]
[Time "10:39:20"]
1. f3 e6
2. g4 Qh4# 1-0
[/pgn]
function ConvertPgn-ForReddit
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateScript({
Test-Path $_
})]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[string]$OutFile,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[string[]]$KeepHeaders = @('Event', 'Date', 'White', 'Black', 'Result')
)
Process
{
# Get file contents as array of strings
$PgnFile = Get-Content -Path $Path
# Get all "headers", e.g. [Event "Computer chess game"]
$Headers = $PgnFile | Where-Object {$_ -match '\[.*\]'}
# Filter "headers", so they contain only the ones we want
$FilteredHeaders = $KeepHeaders | ForEach-Object {
$currHeader = $_
$Headers | Where-Object {$_ -match "\[$currHeader\s+.*\]"}
}
# Get chess moves
$Moves = $PgnFile | Where-Object {$_ -match '^\d+\.'}
# Split them, remove empty lines if any
$SplittedMoves = $Moves | ForEach-Object {$_ -split '(\d+\.)'} | Where-Object {$_}
# Join splitted chess moves: delimeter + actual move. E.g. "1." + "f3 e6 "
$JoinedMoves = 0..($SplittedMoves.Count - 1) | ForEach-Object {
if([bool]!($_ % 2))
{
'{0} {1}' -f $SplittedMoves[$_], $SplittedMoves[$_+1]
}
}
# Create PGN in Reddit-compatible format
$RedditPgn = '[pgn]', $FilteredHeaders, $JoinedMoves, '[/pgn]'
if($OutFile)
{
# If OutFile is specified, save it
$RedditPgn | Set-Content -Path $OutFile
}
else
{
# If not - just output to the pipeline
$RedditPgn
}
}
}
Upvotes: 2
Reputation: 13176
Heyya,
Edit : this is not correct as I don't know how PGN works ...
Here is my attempt. Input file pgn.txt
, output file converted_pgn.txt
. Based on the samples you provided. If you change the number of lines in the input file or the order of them, it all breaks down into pieces :).
Get-Content
will make an array
out of the provided input file. Then you simply keep the cells you want.
To separate the moves, I went for a Split
on spaces.
All of this is not really polished, you could do a much better work with regular expressions if everything is meant to change, input-wise.
#$pgn = Get-Content "pgn.txt"
#this will give us an array like below
$pgn = "--------------------------------------",
"[Event `"Computer chess game`"]",
"[Date `"2015.10.28`"]",
"[Round `"?`"]",
"[White `"White Player`"]",
"[Black `"Black Player`"]",
"[Result `"1-0`"]",
"[BlackElo `"2400`"]",
"[ECO `"A25`"]",
"[Opening `"English`"]",
"[Time `"10:39:20`"]",
"[Variation `"Closed`"]",
"[WhiteElo `"2400`"]",
"[Termination `"normal`"]",
"[PlyCount `"63`"]",
"[WhiteType `"human`"]",
"[BlackType `"human`"]",
"1. f3 e6 2. g4 Qh4# 1-0",
"-------------------------------"
$moves = $pgn[17].Split(" ")
$m1 = $moves[0] + " " + $moves[1] + " " + $moves[2]
$m2 = $moves[3] + " " + $moves[4] + " " + $moves[5] + " " + $moves[6]
"[pgn]$($pgn[1])",$pgn[2],$pgn[4],$pgn[5],$pgn[6],$m1,"$m2 [/pgn]" | Out-File "converted_pgn.txt"
Output :
[pgn][Event "Computer chess game"]
[Date "2015.10.28"]
[White "White Player"]
[Black "Black Player"]
[Result "1-0"]
1. f3 e6
2. g4 Qh4# 1-0 [/pgn]
Upvotes: -1