Mit batch Random auswahl?
Also ich mache ein kleines spiel und da kann man sozusagen "Karten" ziehen, Allerdings möchte ich das sie Komplett random ausgewählt werden von der batch.
Hat da jemand ne ahnung wie das geht? Oder geht das überhaupt?
5 Antworten
Mit %RANDOM% kann man rechnen (Pseudozufallszahl zwischen 0 und 32767 einschließlich).
Karten aus einem Kartenstapel zu entfernen ist nicht ganz einfach, aber auch mit Batch machbar.
(Für "komplett random" braucht man einen Hardware-Zufallsgenerator, der z. B. mit Quantenrauschen arbeitet.)
ich habe mal eine kleine Demo zusammengeschreibselt:
@echo off
setlocal enabledelayedexpansion
set "cols=Kreuz,Pik,Herz,Karo"
set "val=2,3,4,5,6,7,8,9,10,Bube,Dame,Koenig,Ass"
set "cardNum=0"
call :initCardsTabel
rem debug Zeile
set "card" & echo:
rem ziehe 1 Karte
call :drawCard
echo %drawnCard%
echo:
rem test ziehe 6 karten ....aber niemals mehr als Anzahl der Karten-1 (51)...
rem ...sonst bleibt :redraw in einer Endlosschleife hängen
for /l %%k in (1,1,6) do (
call :drawCard
echo Karte %%k = !drawnCard!
)
pause
exit /b
:initCardsTabel
rem kartenstapel initialisieren
for %%a in (%cols%) do (
for %%b in (%val%) do (
rem erhöhe Zähler cardnum
set /a "cardnum+=1"
rem Kartenwerte in einem Array speichern
set "card[!cardNum!]=%%a %%b
) )
exit /b
:drawCard
rem ziehe eine karte
rem zufällige Nummer aus Anzahl der Karten
rem random modulo cardNum +1
:redraw
rem eine neue Zufallszahl generieren ist einfacher als den ganzen Stapel jedesmal neu zu nummerieren
set /a "rNum=%RANDOM% %% cardNum +1"
rem wenn Karte nicht mehr im Stapel -> neue Zufallszahl
if "!card[%rNum%]!"=="" goto :redraw
rem Ergebnis gezogene Karte in variable %drawnCard% zurückgeben
set "drawnCard=!card[%rNum%]!
rem gezogene Karte im Stapel löschen
set "card[%rNum%]="
exit /b
- :initCardsTabel kombiniert die Kartenfarbe cols mit dem Kartenwert val und speichert diese in einem Array
- cardNum gibt zählt automatisch die Karten
- :drawCard ist das Herzstück und gibt in der Variable %dawnCard% farbe und wert einer Karte zurück
die Zufallszahl rNum ergibt sich aus %Random% modulo "Anzahl deer Karten" +1
Das ziehen bewirkt das die Karte nicht mehr im Stapel ist.
In einer höheren Programmiersprache hätte ich wahrscheinlich das Array neu nummeriert.
In Batch ist das zu langsam.
..also frage ich ob die Karte[ZufallsZahl] noch im Stapel ist...
wenn nicht versuche ich eine neue Zahl...
Je mehr Karten man vom Stapel Zieht um so wahrscheinlicher ist es auf eine "tote" Karte zu Treffen .
...aber selbst bei 51 gezogenen Karten ist die Verzögerung (durch das ermitteln einer neuen Zahl) kaum merklich.
Anmerkung zum Kartenstapel:
Der ist nicht gemischt ! ...warum auch ?...wenn wir ohnehin Zufällig wählen.
Vor längerer Zeit habe ich mal %random% getestet. bei 100000 Würfen waren sowohl gleichverteilung der Zahlen, wie auch aufeinanderfolge durchaus akzeptabel .
Um einen bessern Zufallswert zu bekommen kann man ja mehrere Würfe verknüpfen oder mit VBS/Javascript "würfeln"
http://www.robvanderwoude.com/vbstech_data_random.php
die mit Rem debug Zeile gekennzeichnete nachfolgende Zeile kannst Du löschen!
update:
prüft vor der dem Ziehen ob überhaupt noch Karten im Stapel sind verhindert Endlosschleife beim "überziehen":
:drawCard
rem ziehe eine karte
:redraw
rem prüfe ob noch Karten im Stapel
set "card[" >nul 2>&1 || (
rem wenn keine Karten mehr im Stapel Fehlermeldung in Variable %drawnCard%
set "drawnCard=Keine Karte uebrig..."
rem ...und Subroutine mit %errorlevel%=1 abbrechen
exit /b 1)
set /a "rNum=%RANDOM% %% cardNum +1"
if "!card[%rNum%]!"=="" goto :redraw
set "drawnCard=!card[%rNum%]!
set "card[%rNum%]="
exit /b
da Errorlevel einen Fehler meldet, kann man nach Call darauf reagieren.
for /l %%k in (1,1,100) do (
call :drawCard && (
echo Karte %%k = !drawnCard!
rem diverses anderes was sonst noch mit der gezogenen Karte passieren soll
) )
Einen Batch for /l -loop kann man zwar nicht abbrechen, aber man kann dafür sorgen das nur wenn kein Fehler vorhanden ist etwas passiert...
https://ss64.com/nt/syntax-redirection.html
Hier ein kleines Standardscript für Zufallszahlen: (min und max sind eingeschlossen)
SET MIN=1
SET MAX=6
SET /a r=MIN+(MAX-MIN+1)*%random%/32768
Da dein MIN bei den Karten wahrscheinlich immer 0 ist, geht es so noch einfacher:
SET anzahl=5 &rem Die Anzahl deiner Karten
SET /a index= (anzahl * %random%) /32768
Theoretisch erzeugst du dabei mit %random%/32768 eine zufällige Zahl zwischen 0 und 1 und multiplizierst sie mit deinem Maximalwert+1. Da Batch aber keine Kommazahlen unterstützt, muss man als letztes Teilen, sodass erst dort (ab!)gerundet wird.
Hier ein Beispiel um ein zufälliges Element aus einem Array zu bekommen:
For /L %%i in (0 1 10) do set "Karte[%%i]=example#%%i" & set /a "Karte.length=%%i+1"
SET /a index=(%Karte.length% * %random%) /32768
call echo Karte[%index%] = %%Karte[%index%]%%
~Tim
You can control the number's range with:
set /a num=%random% %%100
- will produce number between 0~99.
-----------------------------------------------------------------------------------
Note that this will not be uniformly distributed! Taking the 0~99 example, the numbers 0~67 will occur slightly more often than the numbers 68~99 because 32767 modulo 100 is 67 and not 0 as it would have to be for a uniform distribution.
ich habe mal auf die schnelle einen kleinen Test geschrieben .
@echo off
setlocal enabledelayedexpansion
set "Augen=6"
for %%r in (8,12,23,153,2777,2778,15867,32000) do (
set /a "zufallTim=(Augen * %%r) /32768 +1"
set /a "zufallModulo= %%r %% Augen +1"
echo PseudoZufallszahl= %%r
echo AugenTim=!zufallTim! , AugenModulo=!zufallModulo!
)
pause
exit /b
%%r ersetzt Random um eine kontrollierte Umgebung zu gewärleisten.
Das problem bei Deiner Methode ist, das alle Zufallszahlen unter Augen*random =32769 0 produzieren (1 in der Demo). Das bedeutet ein ein aufeinanderfolgender Anteil von Ergebnissen (32768 /Augen) ist von vornherein erst einmal 0! (nicht gut für die Gleichverteilung)
Du kannst ja die ZahlenListe erweitern und .
Ich habe mal die Methode mit Modulo ein paar mal durchlaufen lassen (hab aus Gewohnheit mal eine Million Durchläufe genommen, aber Batch braucht da etwas länger und ist immer noch am rechnen)
Der derzeitige Zwischenstand:
#1 = 33615
#2 = 33521
#3 = 33849
#4 = 33804
#5 = 33362
#6 = 33869
#7 = 33611
#8 = 33610
#9 = 33772
#10= 33737
Sieht eigentlich ganz gut aus, nur Rest 5 kommt eher seltener vor.
ps: meine Batch nochmal verbessert:
@echo off & rem https://www.gutefrage.net/frage/mit-batch-random-auswahl?foundIn=list-answers-by-user#comment-182708070
setlocal enabledelayedexpansion
for /F "delims=;" %%a in ('"prompt $H; & for %%. in (nix) do call "') do (set "BS=%%a")
set "cols=Kreuz,Pik,Herz,Karo"
set "val=2,3,4,5,6,7,8,9,10,Bube,Dame,Koenig,Ass"
set "cardNum=0"
echo Mische Karten
call :initCardStack 5
rem debug Zeile
::set "card" & echo:
timeout 1 >nul
cls
for /l %%k in (1,1,18) do (
call :drawCard card && (
echo Karte %%k = !drawnCard!
rem diverses anderes was sonst noch mit den Karten passieren soll
) )
pause
exit /b
:initCardStack
rem params: opt. %1 Mixcount
rem kartenstapel initialisieren
for %%a in (%cols%) do (
for %%b in (%val%) do (
rem erhöhe Zähler cardnum
set /a "cardnum+=1"
rem Kartenwerte in einem Array speichern
set "card[!cardNum!]=%%a %%b
) )
for /l %%. in (1,1,%~1) do call :mixCards
exit /b
:mixCards
for /l %%k in (1,1,%cardNum%) do (
call :drawCard card && (
set "tmpCardsStack[%%k]=!drawnCard!"
<nul set /p "=H"
) )
for /l %%k in (1,1,%cardNum%) do (
call :drawCard tmpCardsStack && (
set "card[%%k]=!drawnCard!"
<nul set /p "=%BS%"
) )
exit /b
:drawCard
rem params: %1 name des Kartenstapels
rem ziehe eine karte
rem prüfe ob noch Karten im Stapel
set "%~1[" >nul 2>&1 || (
rem wenn keine Karten mehr im Stapel Fehlermeldung in Variable %drawnCard%
set "drawnCard=Keine Karte uebrig..."
rem ...und Subroutine mit %errorlevel%=1 abbrechen
exit /b 1)
:redraw
set /a "rNum=%RANDOM% %% cardNum +1"
if "!%~1[%rNum%]!"=="" goto :redraw
set "drawnCard=!%~1[%rNum%]!
set "%~1[%rNum%]="
exit /b
Mischt jetzt die Karten:
call :initCardStack 5 5mal
Habe schon überlegt ob ich an langen Winterabenden Knack oder gar MAUMau in Batch schreibe... ;)
Naja Gleichverteilung heißt ja nicht zwingend das in jedem beliebigen Intervall jede Zahl gleich oft vorkommen muss. Die Methoden arbeiten halt unterschiedlich.
Wie schon in meiner Antwort erklärt erzeugt man mithilfe von %random%/32768 eine Zahl zwischen 0 und 1 (wie zB. auch mit Math.random() in JS). Das Intervall [0;1] wird mithilfe der Variablen min & max auf das Intervall [min;max] gebracht.
Ist %random% groß (zB. irgendwas um die 32000), so ist auch %random%/32768 groß (zB. 0.9765625 für 32000) und damit auch das Ergebnis =6.
ergebnis ∼ random (direkt proportional)
Bei modulo hingegen beginnt alle 6 Zahlen die Zählung von vorne, es ist egal bei welchem Vielfachem man sich befindet.
12%6=0; 13%6=1; 14%6=2; ...
18%6=0; 19%6=1; ...
...
36%6=0; 37%6=1; ...
set /a Zufallszahl=%RANDOM%
@echo off
:debut
set /a Zufallszahl=%RANDOM%
echo %Zufallszahl%
pause
goto debut
bei Modulo muss man nicht wissen welche Spanne %Random% hat
set /a "Zufallszahl=%RANDOM% %% gesamtAugen + minAugen "