RockPaperScissors *UPDATE*

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
BatMaster
Posts: 28
Joined: 22 Dec 2010 12:53

RockPaperScissors *UPDATE*

#1 Post by BatMaster » 18 Aug 2011 14:06

I have updated my rockpaperscissors batch file :D to show wins,losses and the amount of times youve have drawn
Here is the code:

Code: Select all

@echo off &setlocal
title RockPaperScissors
set/a wins=0
set/a lost=0
set/a draws=0
:rps1
cls
set/a spr=%random% %% 3
if %spr%==1 GOTO paper
if %spr%==2 GOTO scissors

:rock
cls
SET INPUT=
ECHO Make a choice
echo type stats to see wins and losses
SET /P input=rock/paper/scissors:
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
if /i "%input%"=="stats" goto stats
GOTO rps1

:paper
cls
SET INPUT=
ECHO Make a choice
echo type stats to see wins and losses
SET /P input=rock/paper/scissors:
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
if /i "%input%"=="stats" goto stats
GOTO rps1

:scissors
cls
SET INPUT=
ECHO Make a choice
echo type stats to see wins and losses
SET /P input=rock/paper/scissors:
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
if /i "%input%"=="stats" goto stats
GOTO rps1

:win
cls
SET INPUT=
set/a wins=%wins% + 1
ECHO You have won. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO :eof
GOTO win

:loss
cls
SET INPUT=
set/a lost=%lost% + 1
ECHO You have been defeated. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO :eof
GOTO loss

:draw
cls
SET INPUT=
set/a draws=%draws% + 1
ECHO The game ended in a draw. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO :eof
GOTO draw

:stats
cls
echo you have won %wins% game(s)
echo you have lost %lost% game(s)
echo you have drawn on %draws% occasion(s)
pause
goto rps1

I am currently working on a super rock paper scissors for more hands(e.g.rock,paper,scissors) would be greatly apreciated.BatMaster

jeb
Expert
Posts: 1042
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: RockPaperScissors *UPDATE*

#2 Post by jeb » 19 Aug 2011 12:01

Hi BatMaster,

it's a nice start :)

But you could optimize your code a bit.

If you write more than two times the same code/line you should think about using a functions instead.

Instead of

Code: Select all

:paper
cls
SET INPUT=
ECHO Make a choice
echo type stats to see wins and losses
SET /P input=rock/paper/scissors:


You could use

Code: Select all

:paper
call :GetChoice
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
GOTO rps1

...

:GetChoice
cls
SET INPUT=
ECHO Make a choice
echo type stats to see wins and losses
SET /P input=rock/paper/scissors:
if /i "%input%"=="stats" goto stats
exit /b


jeb

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: RockPaperScissors *UPDATE*

#3 Post by dbenham » 19 Aug 2011 17:26

I agree with jeb.

It looks like you are having fun learning how to do batch programming. Keep at it. :)

Here is a very concise implementation of Rock/Paper/Scissors. I thought you might enjoy learning how it works. You might be able to use some of the tricks I used in your own programming.

Code: Select all

@echo off
setlocal enableDelayedExpansion

set "choices=,1-Rock,2-Paper,3-Scissors"
set "result1=,1-Tied,2-Won,3-Lost"
set "result2=,1-Lost,2-Tied,3-Won"
set "result3=,1-Won,2-Lost,3-Tied"
set /a "Won=0, Lost=0, Tied=0"
set lf=^


rem Do not remove the 2 blank lines above. They are important for defining the lf variable

:play
cls
echo R = Rock
echo P = Paper
echo S = Scissors!lf!
choice /c RPS /n /m "Make your selection:"
set player=%errorlevel%
set /a "computer=(%random% %% 3)+1"
for /f "delims=," %%a in ("!choices:*,%player%-=!") do echo !lf!!lf!You picked %%a
for /f "delims=," %%a in ("!choices:*,%computer%-=!") do echo The computer picked %%a
for /f "delims=," %%a in ("!result%computer%:*,%player%-=!") do set /a "%%a+=1"&echo !lf!You %%a^^^!!lf!!lf!
echo Your record is:!lf!
echo    Wins = %Won%
echo  Losses = %Lost%
echo    Ties = %Tied%!lf!!lf!
choice /m "Would you like to play again"
if %errorlevel%==1 goto :play

I have replaced the SET /P operation with the CHOICE command. It only accepts valid choices that you define. (use CHOICE /? for more info) CHOICE has been around since the early days of DOS, but sadly it is mysteriosly missing from Windows XP and Windows 2000. It reappears as a native command with Vista.

The lf variable contains a linefeed character. It should be accessed using delayed expansion. In this program it is usefull for inserting blank lines in the output. Without using !lf! you would use the following to output a blank line.

Code: Select all

echo(
Most people use echo. instead, but this site has documented how echo. sometimes fails, and echo( is safer.

I have eliminated almost all of the IF statements. One of my favorite things to do in any kind of programming is to use data structures to replace code logic. I use simple data structures stored in variables to provide a map for translating a code into a value. I also use maps to lookup the result. The lookup method I am using is a variation of this technique My variation looks more complicated, but it is actually very straight-forward once you understand:
- string substitution (use HELP SET for more info)
- for /f token processing (use HELP FOR for more info)
- embedding immediate expansion within delayed expansion expressions: This is a very important technique!
The original lookup method uses a very cryptic technique of inserting a command into the search and replace operation.

Another trick I am using is to use the value of one variable as the name of another variable, (or part of a variable name). You might be surprised how often this can be useful.

I used the SET /A += operator to add a value to an existing variable.

In the final FOR line I put multiple commands in the DO clause, seperated by &. Alternatively I could have done:

Code: Select all

for /f "delims=," %%a in ("!result%computer%:*,%player%-=!") do (
  set /a "%%a+=1"
  echo !lf!You %%a^^^!!lf!!lf!
)
which is probably easier to read.

When echoing the ! I had to use ^^^! to escape it because delayed expansion is enabled. If the ! were in quotes then I would escape it like "this^!"


Don't be discouraged if some of the code does not make sense. There is a lot to digest within that small bit of code.

Dave Benham

BatMaster
Posts: 28
Joined: 22 Dec 2010 12:53

Re: RockPaperScissors *UPDATE*

#4 Post by BatMaster » 19 Aug 2011 17:43

@dbenham i have never used the FOR or DO commands
by the way is the choice command still presebt in windows 7(my OS)?
also how can i echo the > symbol?

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: RockPaperScissors *UPDATE*

#5 Post by dbenham » 19 Aug 2011 18:11

DO is part of the FOR command syntax, and FOR is perhaps the most powerful and widely used command in batch programming! :)

I don't have Windows 7, but I believe CHOICE is natively available.

To echo > or any other special character you need to escape it with a ^ caret.

Code: Select all

echo 7 ^> 1 is TRUE

If the special character is within quotes then you don't need to escape it:

Code: Select all

echo "7 > 1 is TRUE"
But see my note in the prior post about escaping ! when delayed expansion is enabled.

Dave Benham

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: RockPaperScissors *UPDATE*

#6 Post by dbenham » 20 Aug 2011 07:47

Here is another version of Rock/Paper/Scissors that will work on XP - I replaced CHOICE with SET /P

Code: Select all


@echo off
setlocal enableDelayedExpansion

set "computerChoices=,1-Rock,2-Paper,3-Scissors"
set "playerChoices=,R-Rock,P-Paper,S-Scissors"
set "result1=,R-Tied,P-Won,S-Lost"
set "result2=,R-Lost,P-Tied,S-Won"
set "result3=,R-Won,P-Lost,S-Tied"
set /a "Won=0, Lost=0, Tied=0"
set lf=^


rem Do not remove the 2 blank lines above. They are important for defining the lf variable

:play
cls
echo R = Rock
echo P = Paper
echo S = Scissors!lf!
set "player="
set /p "player=Make your selection:"
if "!playerChoices:,%player%-=!" == "!playerChoices!" goto :play
set /a "computer=(%random% %% 3)+1"
for /f "delims=," %%a in ("!playerChoices:*,%player%-=!") do echo !lf!!lf!You picked %%a
for /f "delims=," %%a in ("!computerChoices:*,%computer%-=!") do echo The computer picked %%a
for /f "delims=," %%a in ("!result%computer%:*,%player%-=!") do set /a "%%a+=1"&echo !lf!You %%a^^^!!lf!!lf!
echo Your record is:!lf!
echo    Wins = %Won%
echo  Losses = %Lost%
echo    Ties = %Tied%!lf!!lf!
set "player="
set /p "player=Would you like to play again [Y/N]?"
if /i !player! == Y goto :play

Using SET /P requires the user input to be validated. Instead of using a series of IF statements to check the input, I used the map again. I simply compare the choices map with the map after trying to replace the players choice (surrounded by delimiters) with nothing. If the two values are not equal then I know the choice was valid, if they are equal then the choice was invalid. This isn't that big a deal when there are only 3 choices, but it becomes more and more useful as the number of choices increases. Note that string replacement is case insensitive.

An interesting experiment to do with a program like Rock/Paper/Scissors is to script the input with a large number of choices to rapidly test how well the random number generator works. If you provide a large sample size, then the number of Wins, Losses and Ties should be roughly 33% each. Of course this only tests to see if the random number generator is weighted evenly. It doesn't test to see if there are any patterns in the sequence.

For my original version using CHOICE (assume it's called RPS.BAT) the input file should look something like this (named RPS_INPUT.TXT):

Code: Select all

ryryryryryryryryryrn
I only scripted 10 rounds, but using copy and paste it is easy to quickly generate a file with hundreds of rounds.

To run the program using the script:

Code: Select all

<rps_input.txt rps
or

Code: Select all

type rps_input.txt | rps


For the version using SET /P (named RPS2.BAT) the input file should look like (named RPS2_INPUT.TXT):

Code: Select all

r
y
r
y
r
y
r
y
r
y
r
y
r
y
r
y
r
y
r
n

To run the program using the script:

Code: Select all

<rps2_input.txt rps2
or

Code: Select all

type rps2_input.txt | rps2


Dave Benham

BatMaster
Posts: 28
Joined: 22 Dec 2010 12:53

Re: RockPaperScissors *UPDATE*

#7 Post by BatMaster » 20 Aug 2011 10:43

i tried adding a high score thing. but i wont work
here is the code

Code: Select all

@echo off &setlocal
title RockPaperScissors
set/a wins=0
set/a lost=0
set/a draws=0
:makehs
if exist hiscore.rps goto hscr
echo 0>hiscore.rps
echo 0>>hiscore.rps
echo 0>>hiscore.rps
:hscr
< hiscore.rps (
  set /p line1=
  set /p line2=
  set /p line3=
)
:rps1
cls
set/a spr=%random% %% 3
if %spr%==1 GOTO paper
if %spr%==2 GOTO scissors

:rock
call :getchoice
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
GOTO rps1

:paper
call :getchoice
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
GOTO rps1

:scissors
call :getchoice
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
GOTO rps1

:win
cls
SET INPUT=
set/a wins=%wins% + 1
ECHO You have won. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO end
GOTO win

:loss
cls
SET INPUT=
set/a lost=%lost% + 1
ECHO You have been defeated. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO end
GOTO loss

:draw
cls
SET INPUT=
set/a draws=%draws% + 1
ECHO The game ended in a draw. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO end
GOTO draw

:stats
cls
echo you have won %wins% game(s)
echo you have lost %lost% game(s)
echo you have drawn on %draws% occasion(s)
pause
goto rps1

:getchoice
cls
SET INPUT=
ECHO Make a choice
echo type stats to see wins and losses
echo or highscore to see the high scores
SET /P input=rock/paper/scissors:
if /i "%input%"=="stats" goto stats
if /i "%input%"=="highscore" GOTO HIGHSCORES
exit /b

:highscores
echo won = %won%
echo lost = %los%
echo drawn = %drawn%
pause
exit /b
:end
pause
if %wins% gtr %line1% (
  echo %wins%>>hiscore.rps
)
if %lost% lss %line2% (
  echo %loss%>>hiscore.rps
)
if %draws% gtr %line3% (
  echo %draws%>>hiscore.rps
)
exit

the file hiscore.rps is always empty and the hiscores are blank
pleas help
thx in adv. --=BatMaster=--

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: RockPaperScissors *UPDATE*

#8 Post by aGerman » 21 Aug 2011 09:11

First of all delete hiscore.rps. If it exists the default values are not written.
Write ECHO redirections in an opposite style.
ECHO 0>hiscore.rps
tries to redirect the stdIn stream into your file which will fail. Use
>hiscore.rps ECHO 0
instead.
Also you mistook some variable names, you appended the new score to the existing file which ends in a file with 6 lines the first time, 9 lines the next time etc.
Have a look at all the small changes I did to get it run. Try:

Code: Select all

@echo off &setlocal
title RockPaperScissors
set/a wins=0
set/a lost=0
set/a draws=0
:makehs
if exist hiscore.rps goto hscr
>hiscore.rps echo 0
>>hiscore.rps echo 100000000
>>hiscore.rps echo 0
:hscr
<hiscore.rps (
  set /p line1=
  set /p line2=
  set /p line3=
)
:rps1
cls
set/a spr=%random% %% 3
if %spr%==1 GOTO paper
if %spr%==2 GOTO scissors

:rock
call :getchoice
if /i "%input%"=="rock" GOTO draw
if /i "%input%"=="paper" GOTO win
if /i "%input%"=="scissors" GOTO loss
GOTO rps1

:paper
call :getchoice
if /i "%input%"=="rock" GOTO loss
if /i "%input%"=="paper" GOTO draw
if /i "%input%"=="scissors" GOTO win
GOTO rps1

:scissors
call :getchoice
if /i "%input%"=="rock" GOTO win
if /i "%input%"=="paper" GOTO loss
if /i "%input%"=="scissors" GOTO draw
GOTO rps1

:win
cls
SET INPUT=
set/a wins+=1
ECHO You have won. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO end
GOTO win

:loss
cls
SET INPUT=
set/a lost+=1
ECHO You have been defeated. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO end
GOTO loss

:draw
cls
SET INPUT=
set/a draws+=1
ECHO The game ended in a draw. play again
set /p input=y/n:
if /i "%input%"=="y" GOTO rps1
if /i "%input%"=="n" GOTO end
GOTO draw

:stats
cls
echo you have won %wins% game(s)
echo you have lost %lost% game(s)
echo you have drawn on %draws% occasion(s)
pause
goto rps1

:getchoice
cls
SET INPUT=
ECHO Make a choice
echo type stats to see wins and losses
echo or highscore to see the high scores
SET /P input=rock/paper/scissors:
if /i "%input%"=="stats" goto stats
if /i "%input%"=="highscore" GOTO highscores
exit /b

:highscores
echo won = %wins%
echo lost = %lost%
echo drawn = %draws%
pause
exit /b
:end
pause
if %wins% gtr %line1% (
  >hiscore.rps echo %wins%
) else (
  >hiscore.rps echo %line1%
)
if %lost% lss %line2% (
  >>hiscore.rps echo %lost%
) else (
  >>hiscore.rps echo %line2%
)
if %draws% gtr %line3% (
  >>hiscore.rps echo %draws%
) else (
  >>hiscore.rps echo %line3%
)

Regards
aGerman

BatMaster
Posts: 28
Joined: 22 Dec 2010 12:53

Re: RockPaperScissors *UPDATE*

#9 Post by BatMaster » 13 Sep 2011 12:52

@aGerman
sorry for the late reply
your code sorta works
it writes the values to hiscore.rps :D
BUT it wont read the values and are always displayed as 0 :(
no matter what is in hiscore.rps
eg. (hiscore.rps)

Code: Select all

137820
239673
643967

output on batch file

Code: Select all

won = 0
lost = 0
drawn = 0

nitt
Posts: 218
Joined: 22 Apr 2011 02:43

Re: RockPaperScissors *UPDATE*

#10 Post by nitt » 13 Sep 2011 15:41

Hmm... Actually, I don't think I've ever wrote an RPS script. Just had no reason to.

I wrote a Tic Tac Toe one before here a long time ago. I think there is a bug in it, but don't know where and don't really care.

I think I did write an RPS script in Bash, though. Although that's not even Windows, so no point in resurrecting such a simple code.

Post Reply