Java get a random value from 3 different enums - java

I am implementing a simple version of the Cluedo game.
There are 3 types of cards in the game, Character, Weapon and Room. Since one card is nothing more than a String (i.e no functionality or information other than the name is stored in a card), I chose not to have a Card interface and each type extends Card. Rather I had three enums in my game which are:
public enum Character {Scarlett, Mustard, White, Green, Peacock, Plum;}
public enum Weapon {Candlestick, Dagger, LeadPipe, Revolver, Rope, Spanner;}
public enum Room {Kitchen, Ballroom, Conservatory, BilliardRoom, Library, Study, Hall;}
However there is one case where three types of cards are put together and dealt evenly to each player of the game. For example, one player may have a hand of 2 Characters, 2 Weapons and 1 Room, another player may have 3 Rooms and 2 Characters, so long the total number of cards are even it doesn't matter what type that is.
And that's why I wonder if there is a way to randomly choose one single value from all three enums in Java?
Or I shouldn't do this three enums thing in the first place? (Badly designed)

A simple way is to collect all the enum members into a single Object[], then take a random element from it.
Note that an enum can also implement an interface, so you can even have some shared API across all the enums. Typically you'll find yourself writing a lot of switch statements on the value of the enum; those can mostly be replaced by dynamic dispatch against such interface methods. Further note that each enum member can provide its own method implementation.

I think you should keep it like it is, but then put them all in the same list:
List<Enum> enums = new ArrayList<>();
enums.addAll(Arrays.asList(Character.values()));
enums.addAll(Arrays.asList(Weapon.values()));
enums.addAll(Arrays.asList(Room.values()));
And then you take random values of that list. More closely resembles what you do in real life.

You can write something like this:
public enum Character {Scarlett, Mustard, White, Green, Peacock, Plum;}
public enum Weapon {Candlestick, Dagger, LeadPipe, Revolver, Rope, Spanner;}
public enum Room {Kitchen, Ballroom, Conservatory, BilliardRoom, Library, Study, Hall;}
private static final Random RANDOM = new Random(); // random number generator - declared static, because we need only one for whole program
private static final int TOTAL_CARDS = Character.values().length + Weapon.values().length + Room.values().length; // sum up all enum lenghts - in this case, it will be 6 + 6 + 7 = 19
private Enum<?> getRandomCard() {
int randomNumber = RANDOM.nextInt(TOTAL_CARDS); // we "roll a dice" to get some random number. Let's assume that we get "15"
if (randomNumber < Character.values().length) { // is 15 less then 6? No, so we skip this part
return Character.values()[randomNumber];
}
randomNumber -= Character.values().length; // randomNumber = 15 - 6 = 9
if (randomNumber < Weapon.values().length) { // is 9 < 6 ? No, so we skip this
return Weapon.values()[randomNumber];
}
randomNumber -= Weapon.values().length; // randomNumber = 9 - 6 = 3
if (randomNumber < Room.values().length) { // Is 3 < 7 ? Yes!
// so it means that our "dice" has chosen a Room with array index 3
// We call Room.values() to get all room types as an array and then we pick one with index 3
return Room.values()[randomNumber];
}
return null; // should never happen
}
If you don't understand any part of this, please let me know - I'll add some comments.
However, I believe that solution proposed by Oskar Kjellin is far more elegant then this one :)

Related

Trying to create new reference from one ArrayList to a different ArrayList is moving, not copying, my original object

First, I have searched. And all of the answers I have found have been about NOT having the same reference to the same object in two different array lists. so maybe that should tell me I'm doing this wrong in itself? But I'm not sure of how else to manage this.
I am trying to learn Java. And part of that is Swing.
My app I am working on is a simple tournament/bracket app (think march madness. or any simple bracket tree).
Simplified code with just the issue at hand:
class Bracket extends JPanel {
ArrayList<Round> rounds = new ArrayList<Round>();
public void declareWinner(Team team, Game game, Round round) {
// code that gets current round, game, team numbers to calculate the next round, game, team numbers.
int currentRoundNum = rounds.indexOf(round);
int currentGameNum = rounds.get(currentRoundNum).games.indexOf(game);
int currentTeamNum = rounds.get(currentRoundNum).games.get(currentGameNum).teams.indexOf(team);
int nextRoundNum = currentRoundNum + 1;
int nextGameNum = (int) (Math.floor(gameNumber / 2));; // to position the team in the appropriate game slot in the next round
int nextTeamNum = ((gameNumber % 2) == 0) ? 0 : 1; // even game numbers will be the top team in the next round, odd team numbers will be the bottom team in the next round
// here is where things are getting wonky. Trying to set the next round slot to the team that is being declared the winner
rounds.get(nextRoundNum).games.get(nextGameNum).teams.set(nextTeamNum, team);
}
public Bracket(int numRounds) {
// code that creates the bracket structure. # of rounds. the correct # of games depending on the round. etc. Creates empty shell of "placeholder" teams with no name/info.
for(int i = 0; i < numRounds; i++) {
int numGames // set to (number of games of the last round added) * 2
rounds.add(0, new Round(numGames)
}
}
}
class Round extends JPanel {
ArrayList<Game> games = new ArrayList<Game>();
public Round(int numGames) {
for(int i = 0; i < numGames; i++) {
games.add(new Game());
}
}
// more code...
}
class Game extends JPanel {
ArrayList<Team> teams = new ArrayList<Team>();
// more code... creates two teams per each game in constructor
}
class Team extends JPanel {
String teamName;
public Team(String name) {
teamName = name;
add(new JLabel(name));
}
// simple team info, just going for functionality. not much else is here yet.
}
So, lets say in the first round (index 0 of rounds), in the first game (index 0 of rounds.get(0).games), Team 1 (index 0 of rounds.get(0).games.get(0).teams) wins. It's calculating all the stuff correctly. The team is placed in the correct slot. BUT, it completely removes the team from the current position in the round. So now I'm left with only 1 team in the first game of the first round.
It won't let me have the same Team object referenced in both ArrayLists in Rounds[0].Games[0].Teams and Rounds[1].Games[0].Teams. They are nested array lists in each, so 2 different Array Lists. Am I failing because it's bad to extend the JComponents on the classes themselves, and I should completely refactor this?
I can't see exactly what is going on in your example code, as it is not self-contained. However from your description it looks like you are falling foul of the property that Swing components can only be added to one container at a time. If you add the same component to a second container it is automatically removed from the first one.
This doesn't affect the contents of your ArrayLists in the slightest - you can have the same object in as many different ArrayLists as you like.
It also looks like you are muddying the waters by storing this sort of data inside objects which extend Swing components. I suggest you consider separating out a data structure (the Model) from the display components (the View) to make things clearer. Just get the data structure working first, then build the view from it once you have verified it is correct.
On an unrelated note, it looks like you could simplify the start of your code, where before you had:
public void declareWinner(Team team, Game game, Round round) {
// code that gets current round, game, team numbers to calculate the next round, game, team numbers.
int currentRoundNum = rounds.indexOf(round);
int currentGameNum = rounds.get(currentRoundNum).games.indexOf(game);
int currentTeamNum = rounds.get(currentRoundNum).games.get(currentGameNum).teams.indexOf(team);
...
you could replace these lines with
public void declareWinner(Team team, Game game, Round round) {
// code that gets current round, game, team numbers to calculate the next round, game, team numbers.
int currentRoundNum = rounds.indexOf(round);
int currentGameNum = round.games.indexOf(game);
int currentTeamNum = game.teams.indexOf(team);
...
since you already have references to those objects passed in as arguments.

JAVA Square Footage Calculation [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have to Write a program in Java that will take the dimensions of two different homes and calculate the total square footage. The program will then compare the two values and print out a line of text appropriately stating whether it is larger or smaller than the other one.
I am not sure where to even begin. I am new to Java and have only done a Hello World
The first thing you have to do is take input from your user of the length and width of the object. Then you must calculate the sqr ft using the formula :
Length * Width = # of Sqr ft
If you want to do this to two houses you will just need to take two inputs for the second house of the length and width and display that homes total area the same way we did to the first house.
import java.util.*;
public class SqrFoot {
public static void main(String[] args) {
//creates a scanner object
Scanner scan = new Scanner(System.in);
//takes input
System.out.println("Enter the length : ");
double length = scan.nextDouble();
System.out.println("Enter the width : ");
double width = scan.nextDouble();
//calculates and displays answer
System.out.println(length*width + " Sqr ft");
}
}
Does this program take input from the user? If it does, you'll want to use a Scanner to accept user input, as such:
Scanner input = new Scanner(System.in);
You can then use the nextDouble() method to input the house dimensions, like so:
double length1 = input.nextDouble();
Then you can calculate the area of each house:
double area1 = length1 * width1;
Finally, you can use an if-else block to compare the two areas. Here's an example of how you could do it:
if (area1 > area2) {
System.out.println("House 1 is larger than house 2.");
} else if (area1 < area 2) {
System.out.println("House 1 is smaller than house 2.");
} else {
System.out.println("House 1 is the same size as house 2.");
}
This sounds like homework, so I'm not going to do it for you, but I will help you with syntax and let you put it all together.
To store a number you need to declare a variable. Variables come in all different types. There is a:
String Which like the name suggests, is a string of characters like "Hello World". To declare a String named hello that contains "Hello World", type the following:
String hello = "Hello World";
Some important things: String is capitalized. You will learn why later, but just remember it for now. The stuff you were storing in hello started with and ended with a ". As you will see, this is only the case for Strings. Finally, like you may already know, almost every line ends with a ;.
char Which is short for character and stores a single letter (or symbol, but worry about that later). To store the letter 'P' in a variable named aLetter, type the following:
char aLetter = 'P';
Some important things: char like the rest of the variable names I will tell you about, is lowercase. Also, a char starts and ends with an '. Next, I stored a capital P which in Java's mind is completely different than a lowercase p (the point I'm trying to make is everything in Java is case sensitive). Finally, even though my variable name aLetter is two words, I didn't put a space. When naming variables, no spaces. ever.
int Which is short for integer. An int stores a whole number (no decimal places) either positive or negative (The largest number an int can hold is around 2 billion but worry about that later). To store the number 1776 in an int named aNumber, type the following:
int aNumber = 1776;
Some important things: this is pretty straightforward, but notice there aren't any "'s or ''s. In Java "1776" is NOT the same as 1776. Finally, I hope you are noticing that you can name variables whatever you want as long as it isn't a reserved word (examples of reserved words are: int, String, char, etc.)
double Which is pretty similar to int, but you can have decimal points now. To store the number 3.14 in a double named aDecimal, type the following:
double aDecimal = 3.14;
boolean Which is a little harder to understand, but a boolean can only have 2 values: true or false. To make it easier to understand, you can change in your head (not in the code) true/false to yes/no. To store a boolean value of true in a variable named isItCorrect, type the following:
boolean isItCorrect = true;
There are tons more, but that is all you have to worry about for now. Now, lets go to math. Math in Java is pretty self explanatory; it is just like a calculator except times is * and divide is /. Another thing to make sure of, is that you are storing the answer somewhere. If you type 5-6; Java will subtract 6 from 5, but the answer wont be saved anywhere. Instead, do the following:
int answer = 0;
answer = 5-6;
Now, the result (-1) will be saved in the int named answer so you can use it later.
Finally, we have decision making. In computer science, you change sentences like "If the person's age is at least 21, let them into the bar. otherwise, don't let them in." In decision making, you need to turn all of your questions into yes/no questions. When you need to decide a yes/no question, use what are called if statements. The way to write if statements are a little weird: you write the word if then you ask your question in parentheses and you don't but a ;. Instead you put a set of curly braces {}, inside which you write your code that will run if the question in the if statement is true. For example, the bar example above would be, in code, the following:
int age = 25;
boolean letHimIn = false;
if(age>=21)
{
letHimIn = true;
}
Now, the question is, how do you ask a question. To do so, you use the following: <,>,<=,>=,==,!=. These are called comparators because they the things on either side of them. They do the following: < checks if the number on the left is less than the number on the right, > checks if the number on the left is greater than the number on the right, <= checks less than or equal, >= checks greater or equal, == checks if the two numbers are equal, and != checks if the two numbers are not equal. So if(age>=21) asks the question, is the number stored in age greater or equal to 21? If so, do the code in curly braces below. If not, then skip the code. As one more example, the code checks if age is exactly equal to 21 and if so, set letHimInTheBar to true.
int age = 25;
boolean letHimInTheBar = false;
if(age==21)
{
letHimInTheBar = true;
}
Since age is equal to 25 not 21, the code to make letHimInTheBar true never ran which means letHimInTheBar. The final thing to know about decisions is you can use a boolean variable to ask a question directly. In the following example, we are only letting people whose age is NOT equal to 30 into the bar and if we let them into the bar we will print "Welcome to the bar." and if we didn't then we will print "Stay away.". As a reminder ! in Java means not. Meaning that it will flip true to false and false to true.
int age = 25;
int badAge = 30;
boolean letHimIn = false;
if(age!=badAge)
{
letHimIn = true;
}
if(letHimIn)
{
System.out.println("Welcome to the bar.");
}
if(!letHimIn)
{
System.out.println("Stay away.");
}

Java 7 card poker hand evaluator

I'm trying to create a hand evaluator that it will allow me to compare if one hand is better than another. I know there many other post on SO about this topic however many of them are old now and the links no longer link to anything useful. The only way I can think of doing this at the moment is to manually check for every different combination. Eg check for ace of any suit, if this doesn't exist then move to the next combination eg straight flush then 4 of a kind and so on. However, then how would you compare a pair of 2s to a pair of 3s unless you gave an integer to every combination of cards that made something interesting. However, that would very hard to do manually. Is there a better way?
Why hard?
class Card{
String suite;
int value;
....
}
// ...if two combinations are equal, Do for both combinations
int power = 0;
for (Card card: combination){
power += card.getValue()
}
// and compare!
I don't know if this is the best approach, but this could be possible and this is all manually.
Let's say you already have the code to determine which hand the player has in this format:
22s, KKs, 98o, 11o
Now you have an array with three values e.g.:
["K", "K", "s"]
...and another array:
[9, 8, "o"]
You first have to class those hands (like flush, straight etc.) to make sure which one will win (royal flush always win). And after those checks, you have to do some checks in the array. Check if it's suited or offsuit, if "K" is higher than 9 etc.
You can give them values like this:
...
8=8
9=9
10=10
J=11
Q=12
K=13
A=14 (or 1)
And then compare with that. This is a hard algorithm to do it properly without doing so many things manually. I hope someone who has experience with this takes my answer over and recover my mistakes or wrong steps/thoughts.
I would separate the problem for each type of combinations, and assign an integer for a specific occurence of a combination called ie. strength (so a Straight Flush of 5-4-3-2-A is the weakest and K-Q-J-10-9 is the strongest).
Individual combinations can be handled pretty well i think. For a royal flush, you sort your 7 cards by their number descending, and check if the first 5 cards are (A-K-Q-J-10) and if they are of the same suit. A royal flush's strength is indefinite.
For flush you can sort your cards by suit, and check if you can move 4 times in your list of card without witnessing a suit change. A Flush's strength is determined by the highest valued card of it.
A very brief template for the idea:
// main logic of who wins, note it doesn't handle when two players have the same
// combo with same strength
class HandComparator {
public Player compare(Player player1, Player player2) {
HandEvaluator handEvaluator = new HandEvaluator();
Combination player1Combo = handEvaluator.evaluate(player1.hand);
Combination player2Combo = handEvaluator.evaluate(player2.hand);
if (player1Combo.type.equals(player2Combo.type)) {
// note, strength is only taken into account if the two players have
// the same combo (ie both has full house)
if (player1Combo.strength < player2Combo.strength) {
return player2;
} else {
return player1;
}
} else if (player1Combo.type.compareTo(player2Combo.type) < 0) {
return player2;
} else {
return player1;
}
}
}
class Card {
int suit;
int number;
}
class Player {
List<Card> hand;
}
// this is built by CombinationRecognisers. Note, that the member 'strength' is
// related to a specific combination, not all possible hands
class Combination {
enum Type {
ROYAL_FLUSH,
STRAIGHT_FLUSH;
// etc.
}
Type type;
int strength;
}
// implement this for all combinations (ROYAL_FLUSH,STRAIGHT_FLUSH, etc)
interface CombinationRecogniser {
public Combination evaluate(List<Card> hand);
}
/*
* this class holds all CombinationRecognisers and iterates throught them. It
* will stop the moment a combo has been found.
*/
class HandEvaluator {
static List<CombinationRecogniser> recognizers = new ArrayList<CombinationRecogniser>();
static {
recognizers.add(new RoyalFlushRecogniser());
recognizers.add(new StraightFlushRecogniser());
}
public Combination evaluate(List<Card> hand) {
for (CombinationRecogniser recogniser : recognizers) {
Combination combination = recogniser.evaluate(hand);
if (combination != null) {
return combination;
}
}
return null;
}
}
class RoyalFlushRecogniser implements CombinationRecogniser {
#Override
public Combination evaluate(List<Card> hand) {
// code goes here that decides if the given hand is a valid royal flush
return null; // of new Combination() if the given hand is a valid royal flush
}
}
class StraightFlushRecogniser implements CombinationRecogniser {
#Override
public Combination evaluate(List<Card> hand) {
// code goes here that decides if the given hand is a valid straight flush
return null; // of new Combination() if the given hand is a valid royal flush
}
}
This code actually works if you implement the CombinationRecogniser interface for all special combinations. An example:

Custom Dice Generator

Okay so this is for homework. I don't want a complete answer, just someone to help nudge me in the right direction. I am fairly new to Java, so please take it easy :P Okay so my professor is having us do a Dice simulation program with OOP. This is just one portion of the entire problem, but it's the part I'm stuck on, so if you need more I can provide more. But the instructions say:Make a Roll function that will simulate rolling each die, and return the total
This function should only allow values between the appropriate range (i.e. if each die only has 4 faces, only numbers 1 - 4 should be allowed).
What I have so far for this particular function is this:
public double Roll()
{
for(int i = 0; i < numDice; i++)
{
double total = Math.random()*numFaces + 1;
}
return total;
I don't know where to go from here, and I don't know what really to do. This is my first programming class and it's way over my head :P So like I said, if I could just get pointed in the right direction (using dummy talk, cause I'm still having a hard time grasping this whole thing) that would be awesome. And I can provide more of the actual problem if need be.
Just noticed you used double , replace double with int or short. Look at the following tutorial.
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
Your also not so hot with naming conventions. Read this.
http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html
You don't need to roll for each face, only for each dice. Do this.
Please note, for this you could use shorts as well.
public int roll() {
return new Random().nextInt(6) + 1; // Returns number between 1 and 6
}
If you want to roll multiple dice you could do this.
public int roll(int amount) {
int total = 0;
for(int i = 0; i < amount; i++) {
total += new Random().nextInt(6) + 1;
}
return total;
}
You're defining total inside the for loop, which makes it invisible outside it. Also, you're overwriting it (=) instead of adding to it (+=) on each die throw.
public double Roll()
{
double total = 0;
for(int i = 0; i < numDice; i++)
{
total += Math.random()*numFaces + 1;
}
return total;
}
I would also recommend to follow Java style and rename the method roll (methods start with a lowercase letter). The return type also strikes me as odd, I would probably use int in a similar situation.
You could create a class named Die to represent a die. This fits with OOP as 'die' would be one of your nouns. Now think of what configuration and behavior a Die might have. You may wish to have a Die have a configurable number of faces, but default to 6 if it isn't specified. You also want to be able to roll a die. Lets see what we have so far. Implementation left up to the reader.
public class Die {
public Die(){
this(6);
}
public Die(int faces){
//TODO: Write a constructor that takes number faces as an argument.
}
public int roll(){
//TODO: Implement a roll for this die, considering the number of faces.
}
}
Ok, so you have the beginning of a Die class. Now you might think, wouldn't it be cool if I could group die together as Dice (a set of dice if you will).
public class Dice {
public Dice(Die... dice){
//TODO: Implement constructor that takes an array of die objects.
}
public int roll(){
//TODO: Roll all the dice, sum and return the result.
}
}
Next you might think, man wouldn't it be sweet if I could treat Dice and a single Die as a single type, I mean they both can be rolled. Let us create an interface called Rollable that specified the roll behavior and have both Dice and Die implement it.
public interface Rollable {
int roll();
}
Then go back and change the class declarations to be.
public class Die implements Rollable
and
public class Dice implements Rollable
Now code that needs to roll things only needs to worry about Rollable instead of Die or Dice
First of all, you need to declare the total outside the for-loop. Then initialize it to zero.
Then, inside the for-loop. Increase the variable.
If numFaces should be able to be different for each die, you will need to get the value for it inside the for-loop, on each iteration.
Also, you should ask yourself: Which possible values do I want for the total variable? Should it be int or double?
You need to define total outside the for loop and then do total+=… inside the loop

Solving a simple maximization game

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.

Categories

Resources