1

i was creating a rock paper siscors game in batch, i finisced and i wanted to "clean it", becouse it checked every possible combination between the 3 numbers rappresenting rock paper siscors, but i don't know what to do

at first i tried, instead of checking every equal number, i used the "equ" operator, but after that i didn't know what to do, i tried once by making the bigger number the winner, and checked if they were a 0 (paper) and 2 (rock) to make rock weaker than paper on the same "if" function, but the terminal just crashed. i don't know how to do it somwhere else and then check after, i started using batch yesterday, so i dont know praticaly anything. also if i did any error while typing correct me becouse english is not my first language

@echo off
color f
cls
echo welcome to rock paper siscors
:LOOP
echo 0=(paper) 1=(siscors) 2=(rock)
set /p input= select one of the 3:
set /a cpu=%random% %%3
if %input% GTR 2 (echo don't type values bìigger than 2!!!!!!!!!!!!!!)
if %cpu% equ 0 if %input% equ 1 (echo you win!)
if %cpu% equ 0 if %input% equ 2 (echo you lose!)
if %cpu% equ 1 if %input% equ 0 (echo you lose!)
if %cpu% equ 1 if %input% equ 2 (echo you win!)
if %cpu% equ 2 if %input% equ 0 (echo you win!)
if %cpu% equ 2 if %input% equ 1 (echo you lose!)
if %cpu% equ %input% (echo parity!)
if %cpu% equ 0 (echo the cpu selected paper)
if %cpu% equ 1 (echo the cpu selected siscors)
if %cpu% equ 2 (echo the cpu selected rock)
goto :LOOP 
Cip0llø
  • 11
  • 1

3 Answers3

3

Mmm.. These are my thoughs...

Scissors 1 win to Paper 0. Rock 2 win to Scissors 1; but Rock 2 should lose vs Paper. This means that Paper=0 should become Paper=3 IF the opponent is Rock 2. That is:

cpu += (!cpu) * !(input-2) * 3

We will do an extensive use of ! (Boolean NOT) operator, that returns a 1 if its operand is zero, and returns a zero in any other case. In this way, !cpu is 1 if cpu is Paper and is zero in any other case (scissors or rock).

In the same way (input-2) is zero only when input is rock, so !(input-2) returns one only when input is rock (and zero in any other case).

This means that (!cpu) * !(input-2) gives 1 when cpu is Paper AND input is Rock (and 0 in any other case). If you multiply this value times 3 (!cpu) * !(input-2) * 3 and add such a value to cpu: cpu += (!cpu) * !(input-2) * 3 then this expression will convert cpu from Paper=0 to Paper=3 when input is Rock. Ta-Da! :)

We must apply the same treatment to input when it is Paper and cpu is Rock, that is:

set /A cpu += (!cpu) * !(input-2) * 3
set /A input += (!input) * !(cpu-2) * 3

There is no way that both above expressions be executed on the same data, so it is not necessary to insert any IF to prevent to execute one or the other expression.

Also, it is simpler to get the user input via choice command that directly gives a 1..3 result, so we subtract 1 from its returned ERRORLEVEL in order to manage our standard values: 0=Paper, 1=Scissors and 2=Rock:

set /A "cpu=%random% %% 3"
choice /C PSR /N /M "Select [P]aper, [S]cissors or [R]ock: "
set /A "input=%errorlevel% - 1"
set /A "cpu += (!cpu) * !(input-2) * 3"
set /A "input += (!input) * !(cpu-2) * 3"
if %cpu% gtr %input% (echo you lose) else ...

But the SET /A command can execute several operations separated by comma, so above 4 expressions could be given in a single SET /A command. Also, we can get the name of each item via an array of names that can directly give the desired element in a single operation. Here it is the whole thing:

@echo off
setlocal EnableDelayedExpansion

rem Define the "items" array
set "item[0]=Paper" & set "item[1]=Scissors" & set "item[2]=Rock" & set "item[3]=Paper"

:LOOP
    choice /C PSR /N /M "Select [P]aper, [S]cissors or [R]ock: "
    set /A "input=%errorlevel%-1, cpu=%random% %% 3, cpu+=(^!cpu)*^!(input-2)*3, input+=(^!input)*^!(cpu-2)*3"
    if %cpu% gtr %input% (
       echo you lose
    ) else if %cpu% equ %input% (
       echo parity
    ) else (
       echo you win
    )
    echo cpu selected !item[%cpu%]!
    choice /M "Play again? "
    echo/
if not errorlevel 2 goto LOOP

We could also eliminate the IF's commands used to identify the winner via the following trick. If we analyse the possible values of the expression input - cpu, we'll realize that if the result is 2 or 1, you win; if it is 0, is draw; and if it is -1 or -2, you lose. We could directly use these values to define a "result" array with 5 elements this way:

rem Define the "results" array
set "result[2]=you win"
set "result[1]=you win"
set "result[0]=parity"
set "result[-1]=you lose"
set "result[-2]=you lose"

. . .

set /A "res=input-cpu"
echo !result[%res%]!

However, we could diminish the "result" array to just 3 elements if we use the "Sign" function that returns 1, 0 or -1 for positive, zero or negative values, respectively:

set /A "res=input-cpu, res=(res>>31|1)*!!res"

Here it is the final version:

@echo off
setlocal EnableDelayedExpansion

rem Define the "items" array
set "item[0]=Paper" & set "item[1]=Scissors" & set "item[2]=Rock" & set "item[3]=Paper"

rem Define the "results" array
set "result[1]=you win" & set "result[0]=parity" & set "result[-1]=you lose"

:LOOP
    choice /C PSR /N /M "Select [P]aper, [S]cissors or [R]ock: "
    set /A "input=%errorlevel%-1, cpu=%random% %% 3, cpu+=(^!cpu)*^!(input-2)*3, input+=(^!input)*^!(cpu-2)*3, res=input-cpu, res=(res>>31|1)*^!^!res""
    echo !result[%res%]!
    echo cpu selected !item[%cpu%]!
    choice /M "Play again? "
    echo/
if not errorlevel 2 goto LOOP
Aacini
  • 65,180
  • 12
  • 72
  • 108
1
@ECHO OFF
:again
SET /a cpu=(%RANDOM% %% 3) + 1
SET "result="
SET "player="
choice /c rpsq /m "[R]ock [P]aper [S]cissors [Q]uit "
(
 FOR %%e IN (1:Rock 2:Paper 3:Scissors 21 32 13) DO FOR /f "tokens=1,2delims=:" %%b IN ("%%e") DO (
  IF %errorlevel%==%%b SET "player=%%c" 
  IF %cpu%==%%b SET "cpu=%%c" 
  IF %errorlevel%%cpu%==%%b SET "result=beats" 
 )
IF %cpu%==%ERRORLEVEL% SET "result=Draws with"
IF NOT DEFINED result SET "result=Loses to"
)
IF DEFINED player ECHO %player% %result% %cpu%&GOTO again
ECHO Quit?
ECHO Scaredy cat!

GOTO :EOF

Here's my version.

First set cpu to a random number 1 to 3
Then use choice to set errorlevel to 1(Rock) 2(Paper) 3(Scissors) or 4(Quit)
Then notice that the next statement (for...%%e...) is parenthesised, so is parsed as a single block
%%e is set to the value of each of the strings in its parentheses in turn
%%b and %%c are set to the string before and after the : delimiter in the string %%e
If %%b = errorlevel as set by the choice, set player to R/P/S
Ditto cpu from the value set by the set /a
set result to beats if {player choice number}{cpu choice number} is mentioned on the for %%e list
If both player and cpu choices are identical, set result to draws with
if result is still not assigned, it's neither a win nor a draw, so it's a loss
If player wasn't set in the loop, then errorlevel set by the choice must be 4 for Quit, otherwise output the three strings and go back to the beginning.

Magoo
  • 77,302
  • 8
  • 62
  • 84
0

Whilst not a direct fix for your reported problem, here's an rewritten example to study, and which may better suit your purpose:

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Color 0F
Echo Welcome to Rock Paper Scissors

:LOOP
Set /A "Opponent=(%RANDOM% %% 3) + 2, Opponent *= Opponent"
%SystemRoot%\System32\choice.exe /C RPS /N /M "[R]ock, [P]aper, or [S]cissors?"
Set /A "Result=Opponent - %ErrorLevel%"
Set "Opponent=Paper"
If %Result% Lss 4 Set "Opponent=Rock"
If %Result% Gtr 8 Set "Opponent=Scissors"
For %%G In (15 6 2) Do If %Result% Equ %%G Set /P "=You WIN: " 0<NUL
For %%G In (14 8 1) Do If %Result% Equ %%G Set /P "=You LOSE: " 0<NUL
For %%G In (13 7 3) Do If %Result% Equ %%G Set /P "=DRAW: " 0<NUL
Echo Opponent chose %Opponent%
%SystemRoot%\System32\choice.exe /M "Would you like to play again"
If Not ErrorLevel 2 Echo(& GoTo LOOP
Compo
  • 36,585
  • 5
  • 27
  • 39