/* The result of this program is to compute two vectors, each number of * which corresponds to a square on the Monopoly board. The numbers * represent the long term probability of ending up on that square. * There are two vectors to model the two strategies of paying to * immediately get out of jail and staying in as long as possible. There * are times in the game where one or the other is preferable. All of * the rules of the game, including Chance and Community Chest cards and * being sent to Jail on the third double, are taken into account for * these results. * * Jail is split into two spaces. One for just landing on and one for * landing in. The space that is just landed on is at the normal place * in the array. The one representing being in jail is at the end of the * array and is treated separately. * * In the process of calculating these two vectors, we have to compute two * matrices, one for prefered short jail stay and one for prefered long * jail stay. These matrices hold, for each square, an array holding the * probability of reaching each other square on the board in one roll. * * These matrices are used to generate the final probability vectors by * taking a vector initialized with a probability of 1.0 in the Go square * and repeatedly multiplying by the appropriate matrix until the values * in the vector converges. * * By Truman Collins * April 9, 1997 * * Copyright 1997 By Truman Collins */ #include /* The index of the in jail square. */ const int jail_index = 40; /* Indexes for the financial data for properties. */ const int prop_price = 0; const int house_price = 1; const int hotel_price = 2; const int rent_prop = 3; const int rent_1_house = 4; const int rent_2_house = 5; const int rent_3_house = 6; const int rent_4_house = 7; const int rent_hotel = 8; /* Indexes for financial data on the railroads. */ const int rr_cost = 0; const int rr_rent_1 = 1; const int rr_rent_2 = 2; const int rr_rent_3 = 3; const int rr_rent_4 = 4; /* Indexes for financial data on the utilities. */ const int util_cost = 0; const int util_rent_1 = 1; const int util_rent_2 = 2; /* Static data */ static int jail_long; static double eigenvector[41]; static double jail_long_eigenvector[41]; static double jail_short_eigenvector[41]; static double simple_roll_probs[13]; static double prob_roll_is_double[13]; static double markov_matrix[41][41]; static double jail_long_markov_matrix[41][41]; static double jail_short_markov_matrix[41][41]; static int is_a_chance_square[41]; static int is_a_comm_chest_square[41]; static double prob_last_two_rolls_doubles[41]; /* Computed by another program through simulation. */ static double jail_short_prob_last_two_rolls_doubles[41] = { 0.019165, 0.029872, 0.017706, 0.028649, 0.020851, 0.026115, 0.020261, 0.026939, 0.019900, 0.024021, 0.022502, 0.023941, 0.022877, 0.021994, 0.022415, 0.020188, 0.021668, 0.017492, 0.021605, 0.017042, 0.023329, 0.019216, 0.025720, 0.020805, 0.024754, 0.021709, 0.024125, 0.023024, 0.024075, 0.024473, 0.000000, 0.024763, 0.018811, 0.026052, 0.017904, 0.027403, 0.020718, 0.030218, 0.018410, 0.028543, 0.000000 }; static double jail_long_prob_last_two_rolls_doubles[41] = { 0.019554, 0.029881, 0.017728, 0.028759, 0.020882, 0.026610, 0.020306, 0.027174, 0.019967, 0.024284, 0.022582, 0.024579, 0.021549, 0.022856, 0.023235, 0.021294, 0.024598, 0.018795, 0.025794, 0.018126, 0.028700, 0.019693, 0.031988, 0.020806, 0.033137, 0.022303, 0.031569, 0.022806, 0.029914, 0.024055, 0.000000, 0.024347, 0.021044, 0.025596, 0.018391, 0.027055, 0.020542, 0.029982, 0.018184, 0.028726, 0.000000 }; int mod_index(int ind) { return(ind % 40); } void initialize_variables(void) { int i,j; double one_thirtysixth = (1.0 / 36.0); double prob_value; /* Zero simple probability array. */ for(i = 0; i < 13; i++) { simple_roll_probs[i] = 0.0; prob_roll_is_double[i] = 0.0; } /* Zero the markov matrix and the Chance and Community Chest flags. */ for(i = 0; i < 41; i++) { for(j = 0; j < 41; j++) { markov_matrix[i][j] = 0.0; } is_a_chance_square[i] = 0; is_a_comm_chest_square[i] = 0; } /* Depending on if we're computing for the player wanting to stay */ /* in jail or not, copy in the appropriate array for the probability */ /* that the last two rolls were doubles for each square. The */ /* probabilities are based on empirical probability computed with */ /* another program. */ if(jail_long) { for(i = 0; i < 41; i++) { prob_last_two_rolls_doubles[i] = jail_long_prob_last_two_rolls_doubles[i]; } } else { for(i = 0; i < 41; i++) { prob_last_two_rolls_doubles[i] = jail_short_prob_last_two_rolls_doubles[i]; } } /* Now put in the appropriate probabilities in the simple roll probs array. */ /* These represent the probabilities of the various rolls of two dice. For */ /* example, simple_roll_probs[4] is the probability of rolling a 4 with two */ /* dice. */ for(i = 1; i < 6; i++) { prob_value = i * one_thirtysixth; simple_roll_probs[i + 1] = prob_value; simple_roll_probs[13 - i] = prob_value; } simple_roll_probs[7] = 6 * one_thirtysixth; /* Now fill in the array of probabilities that a roll summing to a particular */ /* value is a double. */ prob_roll_is_double[2] = 1.0; prob_roll_is_double[12] = 1.0; prob_roll_is_double[4] = 1.0 / 3.0; prob_roll_is_double[10] = 1.0 / 3.0; prob_roll_is_double[6] = 0.2; prob_roll_is_double[8] = 0.2; /* Note where the Chance and Community Chest squares are. */ is_a_comm_chest_square[2] = 1; is_a_chance_square[7] = 1; is_a_comm_chest_square[17] = 1; is_a_chance_square[22] = 1; is_a_comm_chest_square[33] = 1; is_a_chance_square[36] = 1; } void distribute_chance_probabilities( int orig_square, int new_square ) /* There are 16 Chance cards. 10 of them send the player to */ /* another square. Distribute these probabilities. */ { double one_sixteenth; one_sixteenth = markov_matrix[orig_square][new_square] / 16.0; markov_matrix[orig_square][new_square] = 6.0 * one_sixteenth; markov_matrix[orig_square][39] += one_sixteenth; markov_matrix[orig_square][5] += one_sixteenth; markov_matrix[orig_square][24] += one_sixteenth; markov_matrix[orig_square][11] += one_sixteenth; markov_matrix[orig_square][0] += one_sixteenth; markov_matrix[orig_square][40] += one_sixteenth; /* Some cards are dependent on the Chance square landed on. */ switch(new_square) { case 7 : /* Next Railroad, 3 squares back, and next utility. */ markov_matrix[orig_square][15] += 2.0 * one_sixteenth; markov_matrix[orig_square][4] += one_sixteenth; markov_matrix[orig_square][12] += one_sixteenth; break; case 22 : /* Next Railroad, 3 squares back, and next utility. */ markov_matrix[orig_square][25] += 2.0 * one_sixteenth; markov_matrix[orig_square][19] += one_sixteenth; markov_matrix[orig_square][28] += one_sixteenth; break; case 36 : /* Next Railroad, 3 squares back, and next utility. */ /* Note that for 3 squares back, we land on a */ /* Community Chest square, so we have to distribute */ /* this probability over the likelyhood that the */ /* the card drawn there will send him to Go or Jail. */ markov_matrix[orig_square][5] += 2.0 * one_sixteenth; markov_matrix[orig_square][33] += one_sixteenth * 14.0 / 16.0; markov_matrix[orig_square][0] += one_sixteenth / 16.0; markov_matrix[orig_square][40] += one_sixteenth / 16.0; markov_matrix[orig_square][12] += one_sixteenth; break; default : /* This should never happen. */ fprintf(stderr, "Bad Chance square. It was %d.\n", new_square); } } void distribute_comm_chest_probabilities( int orig_square, int new_square ) /* There are 16 Community Chest cards. 2 of them send the player to */ /* another square. Distribute these probabilities. */ { double one_sixteenth; one_sixteenth = markov_matrix[orig_square][new_square] / 16.0; markov_matrix[orig_square][new_square] = 14.0 * one_sixteenth; markov_matrix[orig_square][0] += one_sixteenth; markov_matrix[orig_square][40] += one_sixteenth; } void compute_markov_array(void) { int i, j; int new_index; double prob_doubled_to_jail; double prob_first_or_second; double prob_third; double sum_of_other_jail_probs = 0; double temp_prob; /* Put in the simple probabilities from each square. */ /* Correct for probability of rolling a double with two previous */ /* doubles and being sent to the in jail square. */ for(i = 0; i < 40; i++) { for(j = 2; j < 13; j++) { new_index = mod_index(j + i); prob_doubled_to_jail = prob_roll_is_double[j] * prob_last_two_rolls_doubles[i]; markov_matrix[i][40] += prob_doubled_to_jail * simple_roll_probs[j]; markov_matrix[i][new_index] += (1.0 - prob_doubled_to_jail) * simple_roll_probs[j]; /* Now take care of the Go to Jail square. */ if(new_index == 30) { markov_matrix[i][jail_index] += markov_matrix[i][30]; markov_matrix[i][30] = 0.0; } /* Now, correct for Chance and Community Chest squares. */ if(is_a_chance_square[new_index]) { distribute_chance_probabilities(i, new_index); } if(is_a_comm_chest_square[new_index]) { distribute_comm_chest_probabilities(i, new_index); } } } /* Now compute the probabilities for leaving the in jail square. */ /* We have two possibilities here. Either we are making the computation */ /* assuming that the player wants to spend time in jail or that he */ /* wants out right away. */ if(jail_long) { /* Here the player waits to get out of jail until he rolls doubles */ /* or has to get out on the third roll. Likely when all or most */ /* properties have been purchased. */ /* Note that we have to separate the possibilites of having just */ /* landed in jail, having been there for one roll, and for two. */ /* The chances of being there on the first roll in jail are more */ /* than one third and the chances of being there on the third are */ /* less than one third. Once sent to jail, there is a 36/36 chance*/ /* that you will see your first roll, a 30/36 chance you will see */ /* your second, and a 25/36 chance you will see your third. So, */ /* the probability that it is your third roll is 25/(36+30+25). */ prob_third = 25.0 / 91.0; prob_first_or_second = 1.0 - prob_third; /* Put in the probabilities for the third roll where we leave */ /* the jail square reguardless of the roll. */ for(j = 2; j < 13; j++) { new_index = j + 10; temp_prob = simple_roll_probs[j] * prob_third; markov_matrix[jail_index][new_index] += temp_prob; } sum_of_other_jail_probs = prob_third; /* Now put in the probabilities for the first or second roll */ /* where we only get out on a double. We just loop through */ /* even rolls, and there is a 1 in 36 chance of getting one */ /* of these with a double. */ for(j = 2; j <= 12; j = j + 2) { new_index = j + 10; temp_prob = (1.0 / 36.0) * prob_first_or_second; markov_matrix[jail_index][new_index] += temp_prob; sum_of_other_jail_probs += temp_prob; } /* Now distribute any Chance or Community Chest squares */ /* that might have been landed on. */ distribute_comm_chest_probabilities(jail_index, 17); distribute_chance_probabilities(jail_index, 22); /* Now the probability that we stay in jail. */ /* Note that it is possible to get immediately back to jail */ /* after rolling out if you land on Community Chest and get */ /* the Go To Jail card. Because of that, we have to add */ /* to the in jail square on this next line. */ markov_matrix[jail_index][jail_index] += 1.0 - sum_of_other_jail_probs; } else { /* Put in the probabilities for leaving jail. Assume the player */ /* pays to get out immediately. Likely early in the game when */ /* there are still some properties to buy. */ for(j = 2; j < 13; j++) { new_index = j + 10; markov_matrix[jail_index][new_index] += simple_roll_probs[j]; /* Correct for Chance and Community Chest squares. */ if(is_a_chance_square[new_index]) { distribute_chance_probabilities(jail_index, new_index); } if(is_a_comm_chest_square[new_index]) { distribute_comm_chest_probabilities(jail_index, new_index); } } } } void compute_eigenvector(void) { int i, j, k; double last_prob_vector[41]; double max_diff; double max_neg_diff; double max_pos_diff; double next_prob_vector[41]; double prob_diffs[41]; /* First zero out the vectors we'll use to calculate with. */ for(i = 0; i < 41; i++) { last_prob_vector[i] = 0; next_prob_vector[i] = 0; prob_diffs[i] = 0; } /* Now put a probability of one into the Go square and start */ /* iterating with the Markov matrix. Each iteration will */ /* advance the probabilities through the vector for a single roll. */ /* for example, after five passes, the probabilities for each */ /* square are the probabilities after five rolls of the dice */ /* with the initial position starting at Go. Eventually, the */ /* resulting probability vector will settle out revealing the */ /* long term probabilites of landing on each square. We compute */ /* an array of differences, which holds the difference for each */ /* element of the vector from one iteration to the next. We use */ /* this to determine when to stop the iterations. */ last_prob_vector[0] = 1.0; do { /* Zero new vector since we're adding into it. */ for(i = 0; i < 41; i++) { next_prob_vector[i] = 0.0; } /* Compute new vector with matrix multiplication. */ for(i = 0; i < 41; i++) { for(j = 0; j < 41; j++) { next_prob_vector[j] += markov_matrix[i][j] * last_prob_vector[i]; } } /* Find differences. */ max_neg_diff = 0.0; max_pos_diff = 0.0; for(i = 0; i < 41; i++) { prob_diffs[i] = last_prob_vector[i] - next_prob_vector[i]; if(prob_diffs[i] < max_neg_diff) { max_neg_diff = prob_diffs[i]; } if(prob_diffs[i] > max_pos_diff) { max_pos_diff = prob_diffs[i]; } } max_diff = max_pos_diff; if(-max_neg_diff > max_diff) { max_diff = -max_neg_diff; } /* Copy to last array in preparation for next iteration. */ for(i = 0; i < 41; i++) { last_prob_vector[i] = next_prob_vector[i]; } } while(max_diff > 1E-15); /* Copy to eigenvector and markov matrix. */ for(i = 0; i < 41; i++) { eigenvector[i] = next_prob_vector[i]; if(jail_long) { jail_long_eigenvector[i] = eigenvector[i]; } else { jail_short_eigenvector[i] = eigenvector[i]; } for(j = 0; j < 41; j++) { if(jail_long) { jail_long_markov_matrix[i][j] = markov_matrix[i][j]; } else { jail_short_markov_matrix[i][j] = markov_matrix[i][j]; } } } } void print_result( char *title ) { int i; double summ = 0.0; summ = 0.0; printf("\n%s\n", title); for(i = 0; i < 41; i++) { printf("%7.5f ", eigenvector[i]); summ += eigenvector[i]; if(i == 9 || i == 19 || i == 29 || i == 39) { printf("\n"); } } printf("\n"); } void main() { /* Do first run assuming the player buys his way out of */ /* jail immediatly. */ jail_long = 0; initialize_variables(); compute_markov_array(); compute_eigenvector(); print_result("Short jail stay eigenvector is:"); /* Do second run assuming the player stays in jail as long */ /* as he can. */ jail_long = 1; initialize_variables(); compute_markov_array(); compute_eigenvector(); print_result("Long jail stay eigenvector is:"); }