import java.util.Random;
/*
* The Game class constructs and allows access to internally processed information stored
* in game objects which represent finitely and variably repeated games of perfect
* information. Each game object holds the utilities of all possible outcomes of all
* possible stage games as well as how the outcomes affect the course of the whole game.
*
* The Game class is meant to be coordinated with player objects by allowing them knowledge
* of rewards and changes in state through the course of the game. The getRewards method and
* changeState method should be used pairwise by another method in this way in order to:
* obtain the outcome, and progress to the next stage of the game, respectively.
*/
public class Game {
private final int NUM_STATES;
private final int NUM_PLAYERS;
private final int NUM_ACTIONS;
private final int NUM_TRIALS;
private final int[][][] PAYOUTS;
private final double[][][] STF;
private int currentState;
private int currentTrial;
/*
* The Game object holds the parameters of the game as final variables and keeps track
* of the current state, the starting one being state zero, and the current trial
* number, which begins at one.
*/
public Game(int states, int players, int actions, int trials, int[][][] rewards, double[][][] probs){
currentState = 0;
currentTrial = 1;
NUM_STATES = states;
NUM_PLAYERS = players;
NUM_ACTIONS = actions;
NUM_TRIALS = trials;
PAYOUTS = rewards;
STF = probs;
}
/*
* The getRewards method takes as input an array of integers representing the sum of
* actions of all the players from which the payout to each player is accessed from
* the PAYOUTS array and outputted.
*/
public int[] getRewards(int[] actions){
return PAYOUTS[currentState][this.getOutcome(actions)];
}
/*
* The changeState method takes as input an array of integers representing the sum of
* actions of all the players from which the next state is obtained stochastically from
* the STF array of probabilities. The integer of the new state is outputted.
*/
public int changeState(int[] actions){
if(currentTrial >= NUM_TRIALS){
return -1;
}
int index = this.getOutcome(actions);
Random r = new Random();
double path = r.nextDouble();
double probSum = 0.0;
for(int i = 0; i < NUM_STATES; i++){
probSum += STF[currentState][index][i];
if(probSum > path){
currentState = i;
currentTrial++;
return i;
}
}
currentState = (NUM_STATES - 1);
currentTrial++;
return (NUM_STATES - 1);
}
/*
* Converts a total list of actions to its outcome number.
*/
private int getOutcome(int[] actions){
int index = 0;
for(int i = 0; i < NUM_ACTIONS; i++){
index += actions[i]*(Math.pow(NUM_PLAYERS, NUM_ACTIONS - (i + 1)));
}
return index;
}
/*
* Returns the parameters and state of the game as six integers in an array. (The
* number of states, the number of players, the number of actions, the number of trials
* the current game-state, and the current trial number)
*/
public int[] getSpecs(){
int[] specs = new int[6];
specs[0] = NUM_STATES;
specs[1] = NUM_PLAYERS;
specs[2] = NUM_ACTIONS;
specs[3] = NUM_TRIALS;
specs[4] = currentState;
specs[5] = currentTrial;
return specs;
}
/*
* Returns true iff the game has not incremented currentTrial, ie no round has yet been
* played.
*/
public boolean isNew(){
if(currentTrial == 1){
return true;
}
return false;
}
/*
* Hands the value of the NUM_PLAYERS field.
*/
public int getNumPlayers(){
return NUM_PLAYERS;
}
/*
* Hands a pointer to the PAYOUTS array.
*/
public int[][][] getPayouts(){
return PAYOUTS;
}
}
|