I'm trying to set up a very basic scoreboard system which should work on every Windows machine without having to install anything (like Python). I'm not sure if a batch file is the way to go there, since it's really outdated, but I just went for it here. This is what I'm trying to do:
- Have a script that lets the operator enter a name and a score
- Sort all of the scores
- Export the top 5 players as a separate txt file (e.g. PointsTopPlayer1.txt, NameTopPlayer1.txt, PointsTopPlayer2.txt, NameTopPlayer2.txt, etc.)
This is what I have so far. A batch-script that let's the user enter a name and some points.
:start
@echo off
cls
echo Please enter data
echo.
set /p _player=name:
:inputpoints
set /p _points=points:
echo %_points%| findstr /r "^[1-9][0-9]*$">nul
if not %errorlevel% equ 0 goto invalid
(echo=%_points%,%_player%)>> data.txt
echo Data saved.
timeout 2 > nul
goto start
:invalid
echo Please enter a valid number.
timeout 1 > nul
goto inputpoints
Now my next guess would be to add a line in the script that sorts the data.txt list, which currently looks like this:
500,Bob
390,Thomas
650,Sam
100,Nick
20,Olivia
Altough it looks like there is no real way to sort numbers except for the sort command, which is not working in this case, because it sorts the numbers alphabetically, which puts 100 before 20. I saw this script online, but I have no clue how to use it to be honest. Next step would be exporting all of the data in seperate files (for the top 5 players), which is something I have no idea how to pull off.
Any ideas appreciated!
CodePudding user response:
If as you say PowerShell is an option for you, you could do this:
$scores = while ($true) {
cls
$player = Read-Host "Please enter the name of a player. (an empty input exits)"
if ([string]::IsNullOrWhiteSpace($player)) {
break # exit the outer loop
}
while ($true) {
$points = Read-Host "Please enter the points for player $player"
if ($points -match '^\d $') { # check if input is numeric
break # exit the inner loop
}
else {
Write-Host "Please enter a valid number" -ForegroundColor Red
Start-Sleep 1
}
}
# output an object with player name and point
[PsCustomObject]@{
Player = $player
Points = [int]$points # convert to int so the sorting later will be numeric
}
}
# if you like, save this all as structured csv file you can open in Excel
$scores | Export-Csv -Path 'X:\Somewhere\scoredata.csv' -NoTypeInformation -UseCulture
# now sort and select the top 5 players
$top5 = $scores | Sort-Object -Property Points -Descending | Select-Object -First 5
# write the textfiles, but it is totally unclear what you want as content of these files..
for ($i = 0; $i -lt $top5.Count; $i ) {
$content = '{0},{1}' -f $top5[$i].Points, $top5[$i].Player
# file PointsTopPlayerX.txt
$content | Set-Content -Path ('X:\Somewhere\{0}TopPlayer{1}.txt' -f $top5[$i].Points, ($i 1))
# file NameTopPlayerX.txt
$content | Set-Content -Path ('X:\Somewhere\{0}TopPlayer{1}.txt' -f $top5[$i].Player, ($i 1))
}
CodePudding user response:
Seeing as this is a comma separated list, you can use Import-Csv
as long as you assign it headers:
@"
500,Bob
390,Thomas
650,Sam
100,Nick
20,Olivia
"@ | ConvertFrom-Csv -Header "Numbers","Name" | Sort-Object -Property {[int]$_.Numbers}
I use ConvertFrom-Csv
for this example but, Import-Csv
will work the same. The next step is using a scriptblock in our Sort-Object
to assign the newly created Numbers column as type int
to sort by.
CodePudding user response:
An idea : Add 100000000 to _points
before saving it. That way, you would have for instance
100000500,Bob
100000390,Thomas
Which will sort quite nicely. If you were analysing the data, you could use
for/f "tokens=1*delims=," %%b in (filename) do (
set /a points=%%b-100000000
echo !points! %%c
)
Noting that you would need to invoke delayedexpansion
as the value of points
is being varied within the code block. See Stephan's DELAYEDEXPANSION link
To get the first n names to a top-scores list is then simple:
for /f "tokens=1,2,*delims=[]," %%u in ('type yourfile^|sort^|find /n "1" ') do if %%u leq 5 (set /a points=%%v-100000000
echo !points!,%%w
)
Again, you'd need delayedexpansion
.
Or, you could follow Stephan's advice
(
for/f "tokens=1*delims=," %%b in (filename) do (
set /a points=%%b 100000000
echo !points!,%%c
)
)>tempfile
(
for /f "tokens=1,*delims=," %%b in ('type tempfile^|sort ') do (set /a points=%%b-100000000
echo !points!,%%c
)
)>filename
which would replace filename
with a sorted version
Note that ((codeblock))>filename
redirects output to a new version of filename
.
But overall, the key to tackling this problem in batch is delayedexpansion
.
CodePudding user response:
A Batch approach that uses nested for loops in conjunction with a temporary array to effect sorting via conditional assessment:
@Echo off & Cls
For /f "delims=" %%e in ('echo Prompt $E^|Cmd')Do Set "\E=%%e"
Setlocal EnableDelayedExpansion
Set "Players[i]=0"
REM substitue %~f0 below with the filepath of the input data
For /f "tokens=1,2 Delims=," %%G in ('%SystemRoot%\System32\Findstr.exe /RC:"^[0-9][0-9]*,[a-zA-Z][a-zA-Z]*[ ]*[a-zA-Z][a-zA-Z]*$" "%~f0"')Do (
%= increment array index =% Set /A "Players[i] =1"
REM index included in variable name to guard against duplicate names
Set "Player[!Players[i]!]=%%H (Player.!Players[i]!)"
%= Assign Score to indexed player =% Set "%%H (Player.!Players[i]!)=%%G"
)
Call:OutputSorted unsorted
Call:SortArray Player Players[i] LEQ
Call:OutputSorted Sorted Low to high
Call:SortArray Player Players[i] GEQ
Call:OutputSorted Sorted High to Low
Goto:eof
:SortArray ======================= <Element_VarName> <Element_Index_VarName> [GEQ|H2L]
REM default sorting method Low to High
Set "SortCompare=LEQ"
REM overide default to High to Low if either of the below given as 3rd Argument
If /I "%~3" == "GEQ" Set "SortCompare=GEQ"
If /I "%~3" == "H2L" Set "SortCompare=GEQ"
Set /a "Max=%2 1"
For /L %%a In (0,1,!Max!)Do (
If !SortCompare! == LEQ (%= Define Sort offset to match Sort Priority =%
Set /A "S_Offset=%%a - 1"
)Else Set /A "S_Offset=%%a"
For /L %%b IN (0,1,%%a)Do (
If not %%b==%%a For %%c in (!S_Offset!)Do (%= Expand nested variable for comparison =%
For /f "tokens=1,2 Delims=;" %%v in ("!%1[%%c]!;!%1[%%b]!")Do IF !%%v! %SortCompare% !%%w! (%= Switch Array Position =%
Set "tmpV=!%1[%%c]!"
Set "%1[%%c]=!%1[%%b]!"
Set "%1[%%b]=!tmpV!"
) ) ) )
Exit /B 0
:OutputSorted
REM output formatting
Echo(%*
Echo(Name %\E%[15G Player %\E%[30G Score
For /f "tokens=2 Delims==" %%G in ('Set Player[')Do (
For /f "tokens=1,3 Delims=(.)" %%i in ("%%G")Do (
Echo(%%i %\E%[15G %%j %\E%[30G = !%%G!
)
)
Echo(
Goto:eof
=== GOTO:EOF ===
[/DATA]
500,Bob
390,Thomas
650,Sam
100,Nick
1020,Nick Sr
20,Olivia
115,Sam
[\DATA]