I've got a very simple question about a game I created (this is not homework): what should the following method contain to maximize payoff:
private static boolean goForBiggerResource() {
return ... // I must fill this
};
Once again I stress that this is not homework: I'm trying to understand what is at work here.
The "strategy" is trivial: there can only be two choices: true or false.
The "game" itself is very simple:
P1 R1 R2 P2
R5
P3 R3 R4 P4
there are four players (P1, P2, P3 and P4) and five resources (R1, R2, R3, R4 all worth 1 and R5, worth 2)
each player has exactly two options: either go for a resource close to its starting location that gives 1 and that the player is sure to get (no other player can get to that resource first) OR the player can try to go for a resource that is worth 2... But other players may go for it too.
if two or more players go for the bigger resource (the one worth 2), then they'll arrive at the bigger resource at the same time and only one player, at random, will get it and the other player(s) going for that resource will get 0 (they cannot go back to a resource worth 1).
each player play the same strategy (the one defined in the method goForBiggerResource())
players cannot "talk" to each other to agree on a strategy
the game is run 1 million times
So basically I want to fill the method goForBiggerResource(), which returns either true or false, in a way to maximize the payoff.
Here's the code allowing to test the solution:
private static final int NB_PLAYERS = 4;
private static final int NB_ITERATIONS = 1000000;
public static void main(String[] args) {
double totalProfit = 0.0d;
for (int i = 0; i < NB_ITERATIONS; i++) {
int nbGoingForExpensive = 0;
for (int j = 0; j < NB_PLAYERS; j++) {
if ( goForBiggerResource() ) {
nbGoingForExpensive++;
} else {
totalProfit++;
}
}
totalProfit += nbGoingForExpensive > 0 ? 2 : 0;
}
double payoff = totalProfit / (NB_ITERATIONS * NB_PLAYERS);
System.out.println( "Payoff per player: " + payoff );
}
For example if I suggest the following solution:
private static boolean goForBiggerResource() {
return true;
};
Then all four players will go for the bigger resource. Only one of them will get it, at random. Over one million iteration the average payoff per player will be 2/4 which gives 0.5 and the program shall output:
Payoff per player: 0.5
My question is very simple: what should go into the method goForBiggerResource() (which returns either true or false) to maximize the average payoff and why?
Since each player uses the same strategy described in your goForBiggerResource method, and you try to maximize the overall payoff, the best strategy would be three players sticking with the local resource and one player going for the big game. Unfortunately since they can not agree on a strategy, and I assume no player can not be distinguished as a Big Game Hunter, things get tricky.
We need to randomize whether a player goes for the big game or not. Suppose p is the probability that he goes for it. Then separating the cases according to how many Big Game Hunters there are, we can calculate the number of cases, probabilities, payoffs, and based on this, expected payoffs.
0 BGH: (4 choose 0) cases, (1-p)^4 prob, 4 payoff, expected 4(p^4-4p^3+6p^2-4p+1)
1 BGH: (4 choose 1) cases, (1-p)^3*p prob, 5 payoff, expected 20(-p^4+3p^3-3p^2+p)
2 BGH: (4 choose 2) cases, (1-p)^2*p^2 prob, 4 payoff, expected 24(p^4-2p^3+p^2)
3 BGH: (4 choose 3) cases, (1-p)*p^3 prob, 3 payoff, expected 12(-p^4+p^3)
4 BGH: (4 choose 4) cases, p^4 prob, 2 payoff, expected 2(p^4)
Then we need to maximize the sum of the expected payoffs. Which is -2p^4+8p^3-12p^2+4p+4 if I calculated correctly. Since the first term is -2 < 0, it is a concave function, and hopefully one of the roots to its derivative, -8p^3+24p^2-24p+4, will maximize the expected payoffs. Plugging it into an online polynomial solver, it returns three roots, two of them complex, the third being p ~ 0.2062994740159. The second derivate is -24p^2+48p-24 = 24(-p^2+2p-1) = -24(p-1)^2, which is < 0 for all p != 1, so we indeed found a maximum. The (overall) expected payoff is the polynomial evaluated at this maximum, around 4.3811015779523, which is a 1.095275394488075 payoff per player.
Thus the winning method is something like this
private static boolean goForBiggerResource ()
{
return Math.random() < 0.2062994740159;
}
Of course if players can use different strategies and/or play against each other, it's an entirely different matter.
Edit: Also, you can cheat ;)
private static int cheat = 0;
private static boolean goForBiggerResource ()
{
cheat = (cheat + 1) % 4;
return cheat == 0;
}
I take it you tried the following:
private static boolean goForBiggerResource() {
return false;
};
where none of the player try to go for the resource that is worth 2. They are hence guaranteed to each get a resource worth 1 every time hence:
Payoff per player: 1.0
I suppose also that if you ask this nice question is because you guess there's a better answer.
The trick is that you need what is called a "mixed strategy".
EDIT: ok here I come with a mixed-strategy... I don't get how Patrick found the 20% that fast (when he commented, only minutes after you posted your question) but, yup, I found out basically that same value too:
private static final Random r = new Random( System.nanoTime() );
private static boolean goForBiggerResource() {
return r.nextInt(100) < 21;
}
Which gives, for example:
Payoff per player: 1.0951035
Basically if I'm not mistaken you want to read the Wikipedia page on the "Nash equilibrium" and particularly this:
"Nash Equilibrium is defined in terms of mixed strategies, where players choose a probability distribution over possible actions"
Your question/simple example if I'm not mistaken also can be used to show why colluding players can do better average payoffs: if players could colude, they'd get 1.25 on average, which beats the 1.095 I got.
Also note that my answers contains approximation errors (I only check random numbers from 0 to 99) and depends a bit on the Random PRNG but you should get the idea.
if the players cannot cooperate and have no memory there is only one possible way to implement goForBiggerResource: choose a value randomly. Now the question is what is the best rate to use.
Now simple mathematics (not really programming related):
assume the rate x represents the probability to stay with the small resource;
therefore the chance for no player going for the big one is x^4;
so the chance for at least one player going to the big one is 1-x^4;
total profit is x + ( 1 - x^4 ) / 2
find the maximum of that formula for 0% <= x <= 100%
the result is about 79.4% (for returning false)
Mmm, I think your basic problem is that the game as described is trivial. In all cases, the optimal strategy is to stick with the local resource, because the expected payoff for going for R5 is only 0.5 (1/4 * 2). Raise the reward for R5 to 4, and it becomes even; there's no better strategy. reward(R5)>4 and it always pays to take R5.
Related
I'm trying to solve the sailors, monkey and coconuts problem by using a recursive algorithm. I want my program to state true or false if it is possible to solve the problem with the values given. In short, the problem is there are s sailors that are stranded on an island with a monkey and y coconuts. Throughout the night, one sailor will wake up, take the y coconuts and sort into s even piles, with one coconut left for the monkey. The sailor then buries one of the piles, and puts the other two piles back together. The next sailor wakes up, and does the same (creates s even piles with one left for the monkey, buries one of the piles, and puts back the other piles).
I also know that 2 sailors requires 7 coconuts, 3 sailors require 79 coconuts and 4 sailors require 1021 coconuts.
I'm having difficulty with my base cases. If I have a case where I have 4 sailors and 81 coconuts for example, my program will say it's possible since 4%81=1. However once the second sailor goes to sort the coconuts in this example, there aren't enough to sort.
Any help or suggestions would be greatly appreciated. Thank you so much!
public class test2 {
public static void main(String[] args) {
int sailors=4, sailorsRemaining=sailors, coconuts=81;
testCoconuts(sailors, sailorsRemaining, coconuts);
}//end main method
public static boolean testCoconuts(int sailors, int sailorsRemaining, int coconutsRemaining){
int s = sailors;
int sr = sailorsRemaining;
int cr = coconutsRemaining;
if (cr%s==1 && sr==0) { //if there are enough coconuts to sort, but no sailors
System.out.println("false1");
return false;
}
else if (cr%s==1 && sr!=0) { //if there are enough coconuts and enough sailors to sort
System.out.print("true1");
return true;
}
if (cr%s!=1) { //if there are not enough coconuts to sort
System.out.println("false2");
return false;
}
else return testCoconuts(s, cr - ((cr-1)/s)-1, sr-1); //recursive step
}
}//end class
Your method takes in (sailors, sailors remaining, coconut count) but your return method sends (sailors, coconut count, sailors remaining). You swapped the order. That still didn't fix it though. I rewrote the if statements and now it works, I think. I did it by answering the following:
What criteria must be met if I can stop recursing and return true?
What criteria must be met if I can continue recursing?.
Answer these 2 and it should be a breeze to complete. And debugging was key to solving it for me.
This problem is somewhat related to number theory. For any number of sailors there is an infinite number of solutions. The idea is to find the smallest one (number of coconuts).
The end result is a power series that results in one equation and two unknowns. Fortunately it is easy to solve visually because the two large components are relatively prime to each other. So the answer boils down to the following:
The smallest number of coconuts to satisfy the solution for N sailors is N(N+1) - (N-1).
So for 3 coconuts 34 = 81 - 2 =79
So all that is needed is a single method to calculate that value and return true or false appropriately.
// all the following print true;
System.out.println(check(7,2));
System.out.println(check(79,3));
System.out.println(check(15621,5));
public static boolean check(int coconuts, int sailors) {
return (int)Math.pow(sailors, sailors+1) - (sailors - 1) == coconuts;
}
Note that there is variation of this problem where when the sailors all get together to do the final division, there is no remainder (i.e. the monkey doesn't get one). In this case the solution is NN - (N-1).
I have a basic framework for a neural network to recognize numeric digits, but I'm having some problems with training it. My back-propogation works for small data sets, but when I have more than 50 data points, the return value starts converging to 0. And when I have data sets in the thousands, I get NaN's for costs and returns.
Basic structure: 3 layers: 784 : 15 : 1
784 is the number of pixels per data set, 15 neurons in my hidden layer, and one output neuron which returns a value from 0 to 1 (when you multiply by 10 you get a digit).
public class NetworkManager {
int inputSize;
int hiddenSize;
int outputSize;
public Matrix W1;
public Matrix W2;
public NetworkManager(int input, int hidden, int output) {
inputSize = input;
hiddenSize = hidden;
outputSize = output;
W1 = new Matrix(inputSize, hiddenSize);
W2 = new Matrix(hiddenSize, output);
}
Matrix z2, z3;
Matrix a2;
public Matrix forward(Matrix X) {
z2 = X.dot(W1);
a2 = sigmoid(z2);
z3 = a2.dot(W2);
Matrix yHat = sigmoid(z3);
return yHat;
}
public double costFunction(Matrix X, Matrix y) {
Matrix yHat = forward(X);
Matrix cost = yHat.sub(y);
cost = cost.mult(cost);
double returnValue = 0;
int i = 0;
while (i < cost.m.length) {
returnValue += cost.m[i][0];
i++;
}
return returnValue;
}
Matrix yHat;
public Matrix[] costFunctionPrime(Matrix X, Matrix y) {
yHat = forward(X);
Matrix delta3 = (yHat.sub(y)).mult(sigmoidPrime(z3));
Matrix dJdW2 = a2.t().dot(delta3);
Matrix delta2 = (delta3.dot(W2.t())).mult(sigmoidPrime(z2));
Matrix dJdW1 = X.t().dot(delta2);
return new Matrix[]{dJdW1, dJdW2};
}
}
There's the code for network framework. I pass double arrays of length 784 into the forward method.
int t = 0;
while (t < 10000) {
dJdW = Nn.costFunctionPrime(X, y);
Nn.W1 = Nn.W1.sub(dJdW[0].scalar(3));
Nn.W2 = Nn.W2.sub(dJdW[1].scalar(3));
t++;
}
I call this to adjust the weights. With small sets, the cost converges to 0 pretty well, but larger sets don't (the cost associated with 100 characters converges to 13, always). And if the set is too large, the first adjustment works (and costs go down) but after the second all I can get is NaN.
Why does this implementation fail with larger data sets (specifically training) and how can I fix this? I tried a similar structure with 10 outputs instead of 1 where each would return a value near 0 or 1 acting like boolean values, but the same thing was happening.
I'm also doing this in java by the way, and I'm wondering if that has something to do with the problem. I was wondering if it was a problem with running out of space but I haven't been getting any heap space messages. Is there a problem with how I'm back-propogating or is something else happening?
EDIT: I think I know what's happening. I think my backpropogation function is getting caught in local minimums. Sometimes the training succeeds and sometimes it fails for large data sets. Because I'm starting with random weights, I get random initial costs. What I've noticed is that when the cost initially exceeds a certain amount (it depends on the number of datasets involved), the costs converge to a clean number (sometimes 27, others 17.4) and the outputs converge to 0 (which makes sense).
I was warned about relative minimums in the cost function when I began, and I'm beginning to realize why. So now the question becomes, how do I go about my gradient descent so that I'll actually find the global minimum? I'm working in Java by the way.
This seems like a problem with weight initialization.
As far as i can see you never initialize the weights to any specific value. Therefore the network diverges. You should at least use random initialization.
If your backprop works on small dataset is there really good assumtion that there isn't problem. When you're suspicious about it you can try your BP on XOR problem.
Are units biased?
I once discuss with guy who doing exactly same thing. Hand digit recognition and 15 units in hidden layer. I saw a network who doing this task well. Her topology was:
Input: 784
First hidden: 500
Second hidden: 500
Third hidden: 2000
Output: 10
You have a sets of images and you nonlinear transform 784 pixels of image into the 15 numbers from <0, 1> interval and you doing this for all images of your set. You hope that you can right separate digit based on these 15 numbers. From my point of view is 15 hidden unit too little for such a task when I assumed you have dataset with thousands of example. Please try for example 500 hidden units.
And learning rate has influence on backprop and can caused problem with convergence.
I was inspired by this question XOR Neural Network in Java
Briefly, a XOR neural network is trained and the number of iterations required to complete the training depends on seven parameters (alpha, gamma3_min_cutoff, gamma3_max_cutoff, gamma4_min_cutoff, gamma4_max_cutoff, gamma4_min_cutoff, gamma4_max_cutoff). I would like to minimize number of iterations required for training by tweaking these parameters.
So, I want to rewrite program from
private static double alpha=0.1, g3min=0.2, g3max=0.8;
int iteration= 0;
loop {
do_something;
iteration++;
if (error < threshold){break}
}
System.out.println( "iterations: " + iteration)
to
for (double alpha = 0.01; alpha < 10; alpha+=0.01){
for (double g3min = 0.01; g3min < 0.4; g3min += 0.01){
//Add five more loops to optimize other parameters
int iteration = 1;
loop {
do_something;
iteration++;
if (error < threshold){break}
}
System.out.println( inputs );
//number of iterations, alpha, cutoffs,etc
//Close five more loops here
}
}
But this brute forcing method is not going to be efficient. Given 7 parameters and hundreds of iterations for each calculation even with 10 points for each parameter translates in billions of operations. Nonlinear fit should do, but those typically require partial derivatives which I wouldn't have in this case.
Is there a Java package for this sort of optimizations?
Thank you in advance,
Stepan
You have some alternatives - depending on the equations that govern the error parameter.
Pick a point in parameter space and use an iterative process to walk towards a minimum. Essentially, add a delta to each parameter and pick whichever reduces the error by the most - rince - repeat.
Pick each pareameter and perform a binary-chop search between its limits to find it's minimum. Will only work if the parameter's effect is linear.
Solve the system using some form of Operations-Research technique to track down a minimum.
I'm writing code to automate simulate the actions of both Theseus and the Minoutaur as shown in this logic game; http://www.logicmazes.com/theseus.html
For each maze I provide it with the positions of the maze, and which positions are available eg from position 0 the next states are 1,2 or stay on 0. I run a QLearning instantiation which calculates the best path for theseus to escape the maze assuming no minotaur. then the minotaur is introduced. Theseus makes his first move towards the exit and is inevitably caught, resulting in reweighting of the best path. using maze 3 in the game as a test, this approach led to theseus moving up and down on the middle line indefinatly as this was the only moves that didnt get it killed.
As per a suggestion recieved here within the last few days i adjusted my code to consider state to be both the position of thesesus and the minotaur at a given time. when theseus would move the state would be added to a list of "visited states".By comparing the state resulting from the suggested move to the list of visited states, I am able to ensure that theseus would not make a move that would result in a previous state.
The problem is i need to be able to revisit in some cases. Eg using maze 3 as example and minotaur moving 2x for every theseus move.
Theseus move 4 -> 5, state added(t5, m1). mino move 1->5. Theseus caught, reset. 4-> 5 is a bad move so theseus moves 4->3, mino catches on his turn. now both(t5, m1) and (t3 m1) are on the visited list
what happens is all possible states from the initial state get added to the dont visit list, meaning that my code loops indefinitly and cannot provide a solution.
public void move()
{
int randomness =10;
State tempState = new State();
boolean rejectMove = true;
int keepCurrent = currentPosition;
int keepMinotaur = minotaurPosition;
previousPosition = currentPosition;
do
{
minotaurPosition = keepMinotaur;
currentPosition = keepCurrent;
rejectMove = false;
if (states.size() > 10)
{
states.clear();
}
if(this.policy(currentPosition) == this.minotaurPosition )
{
randomness = 100;
}
if(Math.random()*100 <= randomness)
{
System.out.println("Random move");
int[] actionsFromState = actions[currentPosition];
int max = actionsFromState.length;
Random r = new Random();
int s = r.nextInt(max);
previousPosition = currentPosition;
currentPosition = actions[currentPosition][s];
}
else
{
previousPosition = currentPosition;
currentPosition = policy(currentPosition);
}
tempState.setAttributes(minotaurPosition, currentPosition);
randomness = 10;
for(int i=0; i<states.size(); i++)
{
if(states.get(i).getMinotaurPosition() == tempState.getMinotaurPosition() && states.get(i).theseusPosition == tempState.getTheseusPosition())
{
rejectMove = true;
changeReward(100);
}
}
}
while(rejectMove == true);
states.add(tempState);
}
above is the move method of theseus; showing it occasionally suggesting a random move
The problem here is a discrepancy between the "never visit a state you've previously been in" approach and your "reinforcement learning" approach. When I recommended the "never visit a state you've previously been in" approach, I was making the assumption that you were using backtracking: once Theseus got caught, you would unwind the stack to the last place where he made an unforced choice, and then try a different option. (That is, I assumed you were using a simple depth-first-search of the state-space.) In that sort of approach, there's never any reason to visit a state you've previously visited.
For your "reinforcement learning" approach, where you're completely resetting the maze every time Theseus gets caught, you'll need to change that. I suppose you can change the "never visit a state you've previously been in" rule to a two-pronged rule:
never visit a state you've been in during this run of the maze. (This is to prevent infinite loops.)
disprefer visiting a state you've been in during a run of the maze where Theseus got caught. (This is the "learning" part: if a choice has previously worked out poorly, it should be made less often.)
For what is worth, the simplest way to solve this problem optimally is to use ALPHA-BETA, which is a search algorithm for deterministic two-player games (like tic-tac-toe, checkers, chess). Here's a summary of how to implement it for your case:
Create a class that represents the current state of the game, which
should include: Thesesus's position, the Minoutaur's position and
whose turn is it. Say you call this class GameState
Create a heuristic function that takes an instance of GameState as paraemter, and returns a double that's calculated as follows:
Let Dt be the Manhattan distance (number of squares) that Theseus is from the exit.
Let Dm be the Manhattan distance (number of squares) that the Minotaur is from Theseus.
Let T be 1 if it's Theseus turn and -1 if it's the Minotaur's.
If Dm is not zero and Dt is not zero, return Dm + (Dt/2) * T
If Dm is zero, return -Infinity * T
If Dt is zero, return Infinity * T
The heuristic function above returns the value that Wikipedia refers to as "the heuristic value of node" for a given GameState (node) in the pseudocode of the algorithm.
You now have all the elements to code it in Java.
Just went through a variant of the game : Rock-Paper-Scissor-Lizard-Spock
I have written a Java code for traditional R-P-S problem, but when I tried extending my code for the newer version of the game (R-P-S-L-S)..I felt my code is terribly bad. Here is a snippet :
if (player1.equals("ROCK") &&
player2.equals("SCISSORS")) {
winner = 1;
}
// Paper covers rock...
else if (player1.equals("PAPER") &&
player2.equals("ROCK")) {
winner = 1;
}
// Scissors cut paper...
else if (player1.equals("SCISSORS") &&
player2.equals("PAPER")) {
winner = 1;
}
else {
winner = 2;
}
I realized the code cant be extended easily for the newer version - as well as for more than 2 players. This is mainly because of multiple if/else or switch/cases. I need some help re-designing my code for achieving the 2 objectives :
Further modification as per R-P-C-L-S problem.
Support for more than 2 players.
I don't need code, just some guidelines should help.
Thanks !!
EDIT : Seems like I was wrong in thinking that this game can be played by more than 2 players. I am sorry for this mistake, please ignore the second requirement.
In, Rock-Paper-Scissor games, it is easy to decide if move a wins against move b using their index at a cycle. So you don't have to manually decide in your code the result of every combination as other answers here suggest.
For the Rock-Paper-Scissor-Spock-Lizard version:
Let's assign a number to each move (0, 1, 2, 3, 4).
Notice that every move beats two moves:
The move previous to it in the cycle (or four cases ahead)
The move two cases ahead in the cycle
So let d = (5 + a - b) % 5. Then:
d = 1 or d = 3 => a wins
d = 2 or d = 4 => b wins
d = 0 => tie
For the Rock-Paper-Scissor version:
let d = (3 + a - b) % 3. Then:
d = 1 => a wins
d = 2 => b wins
d = 0 => tie
Generalization For n >= 3 and n odd:
Let d = (n + a - b) % n. Then:
If d = 0 => tie
If d % 2 = 1 => a wins
If d % 2 = 0 => b wins
The nature of Rock-Paper-Scissors is such that you have to explicitly handle the case for every possible combination of states. So the number of cases you have to cover increases exponentially with the number of players, and polynomially (with the order of the polynomial being the number of players) with the number of options.
Having said that, Java's enums are good for this kind of thing.
Here's my stab at it:
import java.util.Arrays;
import java.util.List;
enum Result {
WIN, LOSE, DRAW;
}
enum Option {
ROCK(SCISSORS),
PAPER(ROCK),
SCISSORS(PAPER);
private List<Option> beats;
private Option(Option... beats) {
this.beats = Arrays.asList(beats);
}
Result play(Option other) {
if beats.contains(other) {
return Result.WIN;
} else if other.beats.contains(this) {
return Result.LOSE;
} else {
return Result.DRAW;
}
}
}
Adding more cases (Lizard and Spock) is consequently relatively simple. Adding more players would be more complicated; among other things, you'd have to determine what the rules of three-player Rock-Paper-Scissors even are, because I have no idea.
i think: 1 beats 2 or 5 loses to the rest. 2 beats 3 or 1 loses to the rest. 3 beats 4 or 2 loses to rest. 4 beats 5 or 3 loses to the rest. 5 beast 1 or 3 loses to the rest. For 3 players, compare the values of 2 players, then compare the winner vs player 3.
Design an enum Choice (ROCK, PAPER, SCISSORS), where each enum has a Set<Choice> which it wins against.
Have each of your players choose one of the choices.
Iterate through your players, and for each one, iterate over all the other players that are after him in the list of players (for player 0, iterate through players 1, 2, 3, etc; for player 1, iterate through players 2, 3, etc.; ...).
For each match, you have three possibilities:
A beats B (the choice of B is in the set of choices that A beats): increment A's score
A and B have the same choice: do nothing
A doesn't beat B: increment B's score
I suggested a better design in an answer to another post. Have a single switch, and switch over a single encoding of every possible combination of moves, and for an encoding use a positional number system with a base that's a power of 2, so that each digit will map directly to a number of bits, and so that bitwise manipulations are intuitive.
Three bits are sufficient for five choices, and although octal would be ideal, the syntax sucks, so use hex. Each hexadecimal digit then represents one of your five moves, with room to spare. A byte is large enough two encode the simultaneous moves of two players, an int for eight, a long for sixteen. It's straightforward. Follow the link for a code example.
This is a basic logic problem. It is small enough you can do a manual truth table ( or skip ahead to a k-map), minimize and get a solution.
So basically, you need to evaluate first, if it is a draw. Then, you need to evaluate winning relative to other players. Doing this without needing to compare against each user can be a confusing task. Since this only has 5 variables, you can find a minimized solution with a K-map.
You will need to evaluate each user, based on which item they chose with a specific algorithm to determine if they win. Note that with more than 2 players, there can be more than one winner if two people choose the same thing but both beat a 3rd player. Or you can consider that a tie, whatever. I'll assume the former. You should check also that all players didn't choose the same item.
So I've done the first part of the algorithm for you when the user you are evaluating has chosen "rock".
In code, this would look like:
rock=0, paper=0, scissors=0, lizard=0, spock=0, win=0, tie=0
if ( someone chose rock ) rock=1
if ( someone chose paper ) paper=1
if ( someone chose scissors ) scissors=1
if ( someone chose lizard ) lizard=1
if ( someone chose spock ) spock=1
// Check if tie / draw, double check these, but I think I got them all
tie=rock && !paper && spock && lizard || rock && paper && scissors ||
rock && paper && lizard || spock && paper && scissors ||
spock && !rock && paper && lizard || !spock && scissors && lizard && paper
if ( tie ) die()
CheckIfUserWins() {
if ( user chose rock ) {
win=rock && !paper && !spock
if ( user chose paper) {
// .... calculate using k-map and fill in
}
return win
Notice that win=rock && !paper && !spock is exactly what would be expected based on the graphic of what beats what at the link you provided. So you can go to that graphic and pretty quickly fill in the rest of the equations.
This solution is not dependent on any number of players other than to say "someone chose X". So it should scale to > 5 players, etc.
The shortest way:
var n = 5; // Rock, Paper, Scissors, Lizard-Spock
function calculate(x, y, n) {
return 1 - ((n + x - y) % n) % 2;
}
function getWinner(p1Gestrure, p2Guesture) {
if(p1Gestrure === p2Guesture) {
return - 1; // tie
}
return this.calculate(p1Gestrure, p2Guesture); // 0: win for p1. 1: win for p2.
}
I've created a cli game, please feel free to take a look there.
https://github.com/julianusti/rpc-es6