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