I need a way to use batch to look at every line of a text file, and delete half of the lines in that text file, choosing which lines to delete at random.
This is to simu
This method preserve the order of original lines.
@echo off
setlocal EnableDelayedExpansion
rem Generate an array of line numbers with all file lines
for /F "delims=:" %%a in ('findstr /N "^" input.txt') do (
set "n[%%a]=%%a"
set "lines=%%a"
)
rem Delete half the numbers in the array in random order
set /A halfLines=lines/2
for /L %%n in (%lines%,-1,%halfLines%) do (
set /A rnd=!random!*%%n/32768+1
set /A n[!rnd!]=n[%%n]
set "n[%%n]="
)
rem Reorder the resulting elements
for /F "tokens=2,3 delims=[]=" %%i in ('set n[') do (
set "l[%%j]=1"
set "n[%%i]="
)
rem Copy such lines
(for /F "tokens=1* delims=:" %%a in ('findstr /N "^" input.txt') do (
if defined l[%%a] (
echo(%%b
set "l[%%a]="
)
)) > output.txt
The design specified in the question is flawed. You cannot randomly delete half the lines because then for any given game, you might end up with 2 winners, or no winners. The input file must have a structure that specifies which contestants play each other, and then a winner must be randomly selected from each contest.
The solution below assumes each line represents a player, and for each round, line 1 plays line 2, line 3 plays line 4, etc. The number of lines must always be a power of 2 >=2 (2, 4, 8, 16, 32, 64, ...)
@echo off
setlocal enableDelayedExpansion
set "file=tournament.txt"
for /f %%C in ('find /c /v "" ^<"%file%"') do (
for /l %%N in (1 2 %%C) do (
set /p "p1="
set /p "p2="
set /a "1/(!random!%%2)" 2>nul&&echo !p1!||echo !p2!
)
) <"%file%" >"%file%.new"
move /y "%file%.new" "%file%" >nul
The outer loop counts the number of lines. The inner loop counts the odd numbers from 1 to the count, so it iterates exactly (line count divided by 2) times. The inner loop has input redirected to the source file, and the output redirected to a temporary file.
Each inner loop iteration represents a game in the tournament. SET /P is used to load the player names into variables. Then 1 is divided by a random number modulo 2, which will result in a divide by 0 error 50% of the time. The error message is redirected to nul, and then conditional operators are used to print one or the other player to the output file.
Upon completion of the loop, the temporary file is moved to replace the original file.
One way to do this would be to create simple files for each character. Then use a random script to do something like this:
set/a number=%random% * (number of people there are) / 32768 + 1
set status=dead
call person%number%.bat (Each file should contain a set status=live script)
if %status% == live (
set/a peopletokill=%peopletokill%-1
del person%number%.bat
)
if %peopletokill% == 0 (
exit
)
Something like that could work.
This is a native Windows batch script using Jscript to randomise the lines of a text file and print half of them.
The size of the file is limited to the RAM that Jscript is able to request.
@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScript comment
:batch file portion
@echo off
if "%~1"=="" (
echo(Purpose: Randomises a text file - prints every 2nd random line
echo(
echo(Syntax: "%~0" "inputfile.txt" ^> "outputfile.txt"
echo(
pause
goto :EOF
)
:: Randomises a file
::
:: Reads the lines of file %1 into a jscript sparse array
:: with a random number and space before each line.
:: The array is sorted using the random numbers,
:: and the randomised lines are printed - prints every 2nd random line
@echo off
cscript //E:JScript //nologo "%~f0" %*
:pause
exit /b
************ JScript portion ***********/
e=0;
var array = new Array();
fso = new ActiveXObject("Scripting.FileSystemObject");
input=fso.OpenTextFile((WScript.Arguments(0)),1);
while (!input.AtEndOfStream) {
array[e]=Math.random()+" "+input.ReadLine();
e++;
}
input.close();
array.sort();
var re = new RegExp(".*? (.*)");
c=0;
while (c<e) {
var arr = re.exec(array[c])
if (c % 2) WScript.StdOut.Writeline(RegExp.$1);
c++;
}
@echo off
setlocal enableextensions disabledelayedexpansion
set "inputFile=list.txt"
set "outputFile=remaining.txt"
set "odd=1"
>"%outputFile%" (
for /f "tokens=1,* delims=¬" %%a in ('
"cmd /q /v /e /c"
for /f "usebackq delims=" %%l in ("%inputFile%"^) do (
set /a 100000000+%random%*^!random^!^&echo(¬%%l
^)
""
^| sort /+3
') do if not defined odd ( set "odd=1" ) else (
echo %%b
set "odd="
)
)
type "%outputFile%"
This will take the input file, for each line echo its contents with a random prefix, sort this list using the random number as key and from this list echo only odd lines to output file.
edited I've seen the Aacini's answer and, yes, it can be useful to have the output in the same order than the input. If this is the case, just to have another version
@echo off
setlocal enableextensions disabledelayedexpansion
set "inputFile=list.txt"
set "outputFile=remaining.txt"
setlocal enabledelayedexpansion
rem Retrieve and calculate line limits to process
for /f %%a in ('^<"!inputFile!" find /c /v ""') do set /a "nLines=%%a", "nLimit=%%a/2"
rem Prepare an array with shuffled line numbers
for /l %%a in (1 1 %nlines%) do (
set /a "sel=!random! %% %%a + 1"
if !sel!==%%a ( set "r[%%a]=%%a" ) else (
for %%s in (!sel!) do set /a "r[%%a]=!r[%%s]!", "r[%%s]=%%a"
)
)
rem Read input file and output selected lines
<"!inputFile!" >"!outputFile!" (
for /l %%a in (1 1 %nLines%) do (
set /p "line=" || set "line="
if !r[%%a]! leq %nLimit% echo(!line!
)
)
type "!outputFile!"
Including a PowerShell solution for completeness.
$lines = Get-Content input.txt
$lines | foreach { $i=0 } {
[PSCustomObject]@{index=$i;line=$_}
$i++
} |
Get-Random -count ($lines.length / 2) |
sort index |
select -Expand line |
Set-Content output.txt
This reads the input file, and builds an array of custom objects associating the text with its line number. The script uses Get-Random
to select half the lines. Get-Random does not preserve order, so the script then sorts on the original line number. It then extracts the original lines in order and writes them out.
This script above requires PowerShell 3.0 for the PSCustomObject. Version 3.0 comes preinstalled on Windows 7 and above. If you need to run on Vista, you can use PSObject instead, as shown in this answer or this blog post.
Note that if the line order doesn't matter, then this script becomes much simpler.
$lines = Get-Content input.txt
$lines | Get-Random -count ($lines.length / 2) | Set-Content output.txt
To run a PowerShell script from a batch file or the CMD prompt, use the following.
powershell.exe -ex bypass -file yourscript.ps1
Or if your script fits entirely in one line, no need to create a separate ps1
powershell.exe -c "$l = gc input.txt; $l | Get-Random -c ($l.length / 2) | sc output.txt"