When I step through the program using the debugger it works fine and the displays the correct results. However, when I run the program it displays some extremely high number for homeTeamRuns and awayTeamRuns. I can't seem to figure out the problem.
class BallGame
{
//Fields
private string output = "";
int outs = 0;
int runs = 0;
int homeTeamRuns = 0;
int awayTeamRuns = 0;
bool homeTeam = false;
bool awayTeam = false;
int[] innings = new int[12]; //Innings
//Properties
public string Output
{
get
{
return output;
}
set
{
output = value;
}
}
//Game Simulator
public void playBall()
{
int[] play = new int[4]; //Bases
for(int i = 1; i <= 2; i++)
{
homeTeam = false;
awayTeam = false;
if(i%2 == 0)
homeTeam = true;
else
awayTeam = true;
//Half Inning
while (outs < 3)
{
bool yourOut = false, single = false, double1 = false, triple = false, homerun = false;
Random rnd1 = new Random();
int randomNum = rnd1.Next(1, 101);
//
if (randomNum >= 1 && randomNum <= 50) //50% chance
yourOut = true;
else if (randomNum > 50 && randomNum <= 60) //10% chance
single = true;
else if (randomNum > 60 && randomNum <= 92) //42% chance
double1 = true;
else if (randomNum > 92 && randomNum <= 97) //5% chance
triple = true;
else if (randomNum > 97 && randomNum <= 100) //%3 chance
homerun = true;
if (yourOut == true)
{
outs++;
}
else if (single == true)
{
Shift(play);
runScored(play);
play[0] = 1;
}
else if (double1 == true)
{
Shift(play);
runScored(play);
Shift(play);
runScored(play);
play[1] = 1;
}
else if (triple == true)
{
Shift(play);
runScored(play);
Shift(play);
runScored(play);
Shift(play);
runScored(play);
play[2] = 1;
}
else if (homerun == true)
{
Shift(play);
runScored(play);
Shift(play);
runScored(play);
Shift(play);
runScored(play);
Shift(play);
runScored(play);
play[3] = 1;
}
play[3] = 0;
if (outs == 3)
{
play[0] = 0;
play[1] = 0;
play[2] = 0;
play[3] = 0;
}
}
outs = 0;
}
output = "Home Team Runs: " + homeTeamRuns + " Away Team Runs: " + awayTeamRuns;
}
//Shift Array
public int[] Shift(int[] array)
{
for (int i = array.Length -1; i >= 1; i--)
{
array[i] = array[i - 1];
}
array[0] = 0;
return array;
}
public void runScored(int[] array)
{
if(array[3] == 1)
{
if(awayTeam == true)
awayTeamRuns++;
else if(homeTeam == true)
homeTeamRuns++;
}
}
}
You are creating a new instance of the Random class on each iteration of your loop. The documentation for the constructor you are using says:
Initializes a new instance of the Random class, using a time-dependent default seed value.
As the loop executes very quickly when you aren't debugging you end up with the same seed being used on many iterations and thus the call to Next returns the same value each time. If that value happens to be a value that will increment the score then the score will be incremented on many iterations of the loop.
Moving the line
Random rnd1 = new Random();
above your for loop will construct one Random instance and then create a new random number for each call to Next.
You can't reproduce this when running in the debugger as the clock has moved on by the time you create a new Random instance and thus the seed has changed.
More information on the Random instance returning the same value can be found in this StackOverflow post. Also, Jon Skeet has a blog post covering pitfalls when using the Random class which is well worth a read.
Related
I'm trying to use Yield so my functions wait to spawn more enemies in scene if the max amount has been reached. But now the functions skips all this while cycle in its entirety.
I have never use yield so maybe I'm understanding wrong what it does while reading the documentation.
Also maybe there is a better way to do it.
while ( i < clonesASpawnear.Length)
{
if (j <= endList)
{
if (clonesASpawnear[i] == null)
{
if (sPCurrent == sPMax)
{
sPCurrent = 0;
}
yield return new WaitUntil(() => aliveEnemies < maxAmmoutOfEnemiesOnStage);
clonesASpawnear[i] = Instantiate(enemyTypeList[j], spawnPoints[sPCurrent].transform.position, Quaternion.Euler(0, 0, 0)) as GameObject;
clonesASpawnear[i].SetActive(true);
clonesASpawnear[i].GetComponent<EnemyMovement_DCH>().player = Target;
aliveEnemies += 1;
clonesASpawnear[i].GetComponent<EnemyDamageHandler_DCH>().SpawnerEnemies = this;
j++;
i++;
sPCurrent++;
}
}
else
{
j = startList;
}
}
}
Edited as requested: here is the update where the function is called
void Update()
{
if (pdh.playerIsDead == false && roundOver==false)
{
playerAliveTime += Time.deltaTime;
}
if (waveNumer <= 3 )
{
timeForNextSpawn -= Time.deltaTime;
if (timeForNextSpawn <= 0 && aliveEnemies == 0)
{
nextWaveTextUI.text = nextWaveText;
int waitT = (int)waitTimeForNewWave;
StartCoroutine(delayXSeconds(waitT));
timeForNextSpawn = waitTimeForNewWave;
auxWaveThisRound--;
waveNumer++;
spawnEnemies();
}
}
else
{
if(aliveEnemies == 0 && auxWaveThisRound <= 0)
{
clearedRoundTextUI.text = clearedRoundText;
roundOver = true;
StartCoroutine(waiterReset());
}
}
accuracy = successfulProjectiles / projectileFired;
}
And the complete function where the above code is
IEnumerator spawnEnemies()
{
int percentForWave=0;
int percentForType=0;
int TotalEnemies = (int)enemySpawnsThisRound;
if (waveNumer == 1)
{
Debug.Log("Entro al wave 1");
percentForWave = 20;
percentForType = 20;
startList = 0;
}
if (waveNumer == 2)
{
Debug.Log("Entro al wave 2");
percentForWave = 70;
percentForType = 70;
startList = endList;
}
if (waveNumer == 3)
{
Debug.Log("Entro al wave 3");
percentForWave = 10;
percentForType = 10;
startList = endList;
}
int enemiesThisWave = Decimal.ToInt32(Math.Round(TotalEnemies * ((decimal)percentForWave / 100), 1));
int enemiesForType = Decimal.ToInt32(Math.Round(lenghtList * ((decimal)percentForType / 100), 1));
endList = enemiesForType + startList;
clonesASpawnear = new GameObject[enemiesThisWave];
int i = 0;
int j = startList;
while ( i < clonesASpawnear.Length)
{
if (j <= endList)
{
if (clonesASpawnear[i] == null)
{
if (sPCurrent == sPMax)
{
sPCurrent = 0;
}
yield return new WaitUntil(() => aliveEnemies < maxAmmoutOfEnemiesOnStage);
clonesASpawnear[i] = Instantiate(enemyTypeList[j], spawnPoints[sPCurrent].transform.position, Quaternion.Euler(0, 0, 0)) as GameObject;
clonesASpawnear[i].SetActive(true);//lo activo
clonesASpawnear[i].GetComponent<EnemyMovement_DCH>().player = Target;
aliveEnemies += 1;
clonesASpawnear[i].GetComponent<EnemyDamageHandler_DCH>().SpawnerEnemies = this;
j++;
i++;
sPCurrent++;
}
}
else
{
j = startList;
}
}
}
Do not use a while loop. Instead, use "Update()".
void Update() {
if (aliveEnemies < maxAmmoutOfEnemiesOnStage && j <= endList)
{
if (clonesASpawnear[i] == null)
{
if (sPCurrent == sPMax)
{
sPCurrent = 0;
}
clonesASpawnear[i] = Instantiate(enemyTypeList[j], spawnPoints[sPCurrent].transform.position, Quaternion.Euler(0, 0, 0)) as GameObject;
clonesASpawnear[i].SetActive(true);
clonesASpawnear[i].GetComponent<EnemyMovement_DCH>().player = Target;
aliveEnemies += 1;
clonesASpawnear[i].GetComponent<EnemyDamageHandler_DCH>().SpawnerEnemies = this;
j++;
i++;
sPCurrent++;
}
}
else
{
j = startList;
}
}
This is the idea of the Update frame. Logic in here is hit every frame. It will only continue if the number of enemies is below the max. This method MUST go into a class that extends MonoBehaviour, or else it will not be invoked. Given the intended logic, it makes sense that this would be attached to some master "game monitoring" GameObject that is managing the meta of the game along with win states etc.
EDIT (After more content):
Heres one problem "spawnEnemies();". That is an IEnumerator, but you don't use "StartCoroutine(spawnEnemies())". So the method will literally not execute. There will be no errors, but nothing in the method will occur.
If you have:
bool[] arrayExample = {false, true, false, false, true, true, false, false, true, true, true};
Given an n value, I am looping through the array to find which index has a True value.
For example: if n = 1: I want to return all index's that have 1 True value. Therefore the return list would be {1, 4, 5, 8, 9, 10}
if n = 2: I want to return list to have the index's where there is 2 True values in a row: {4, 8, 9}
Also, I would like to do this for n=3?
Is there a cleaner/more performant way to run this code?
List<int> indices = new List<int>();
bool counter = false;
if (n == 1)
{
for (int i = 0; i < arrayExample.Length; ++i)
{
if (arrayExample[i])
{
indices.Add(i);
}
}
}
else if (n == 2)
{
for (int i = 0; i < arrayExample.Length; ++i)
{
if (counter == true && arrayExample[i] == false)
{
counter = false;
}
if (arrayExample [i])
{
if (counter == false)
{
counter = true;
}
else
{
indices.Add(i);
counter = false;
}
}
}
}
Here's an idea:
Take your example array: [F,T,F,F,T,T,F,F,T,T,T]
Go from end to start, and mark how many True values you have encountered at each position: [0,1,0,0,2,1,0,0,3,2,1].
Now you can get the answer for any n: return every index where array[index] >= n
The speed of your code is probably okay; however, you can simplify the algorithm
const bool F = false, T = true;
bool[] arrayExample = { F, T, F, F, T, T, F, F, T, T, T };
var indices = new List<int>();
int count = 0;
int n = 2;
int delta = n - 1;
for (int i = 0; i < arrayExample.Length; i++) {
if (arrayExample[i]) {
count++;
if (count >= n)
indices.Add(i - delta);
} else
count = 0;
}
This works for any n. Count how many succeeding true values you encountered and add an index whenever this count is a least equal to the required sequence length. Since you want the index of the first index in your sequence, you must subtract n - 1.
This algorithm directly yields the indices in ascending order, does not need a temporary array and has no nested loops. I.e., it has a time performance of O(n) (here n stands for the length of the input).
How about:
static void Main(string[] args)
{
bool[] arrayExample = { false, true, false, false, true, true, false, false, true, true, true };
List<int> result = Foo(1, arrayExample);
foreach(var num in result)
Console.WriteLine(num);
Console.ReadKey();
}
static List<int> Foo(int num,bool[] arr)
{
List<int> indices = new List<int>();
int count = 0;
for (int i = 0; i < arr.Length; i++)
{
if(arr[i])
{
for (int j = 0; j < num; j++)
{
if(!arr[i+j])
{
count = 0;
break;
}
count++;
}
if (count > 0)
indices.Add(i);
}
}
return indices;
}
You can try the idea given by #Dennis_E like this:
int i, j;
int[] rep = new int[arrayExample.Length];
if (rep[arrayExample.Length - 1])
rep[arrayExample.Length - 1] = 1;
for (i = arrayExample.Length - 2; i >= 0; i--)
if (arrayExample[i])
rep[i] = rep[i + 1] + 1;
for (i = 0 ; i < arrayExample.Length; i++)
if (count == n)
indices.Add(i);
Just figured I'd throw my code in the mix (never quick enough to actually provide any useful insights, anyhow):
public static IEnumerable<int> TrueIndicesCounter(int counter, List<bool> booleans)
{
if (counter <= 0) return new List<int>();
var truths = new List<int>();
for (var booleanIndex = 0; booleanIndex < booleans.Count; booleanIndex++)
{
var boolean = booleans[booleanIndex];
if (boolean != true) continue;
if (counter == 1)
{
truths.Add(booleanIndex);
continue;
}
var counterIndex = 1;
var counterSatisfied = false;
while (counterIndex <= counter)
{
var targetBoolean = booleans.ElementAtOrDefault(booleanIndex + counterIndex);
if (targetBoolean != true) break;
counterIndex++;
if (counterIndex != counter) continue;
counterSatisfied = true;
break;
}
if (counterSatisfied)
truths.Add(booleanIndex);
}
return truths;
}
Find the sum of all prime numbers not greater than N. For example if user input 5 then prime numbers are 2,3,5 and their sum is 10. It is not passing 4 test cases in which two of them are exceeding the time limit. I have tried several test cases and my code is working fine on them. Here is my code.
public static long sieve_of_eratosthenes(long n)
{
if (n == 1)
{
// If the user input 1.
return (0);
}
else
{
long sum = 0;
bool[] array = new bool[n + 1];
for (long i = 2; i <= n; i++)
{
// Setting all values to true.
array[i] = true;
}
// Eliminating the composite numbers.
for (long j = 2; j < Math.Sqrt(n); j++)
{
if (array[j])
{
long multiple = 1;
for (long k = (j * j); k <= n; k = (j * j) + (j * (multiple++)))
{
array[k] = false;
}
}
}
//Now we have the prime numbers. We just have to add them.
for (int z = 2; z <= n; z++)
{
if (array[z])
{
sum = sum + z;
}
}
return (sum);
}
}
static void Main(string[] args)
{
int noofcases = int.Parse(Console.ReadLine());
for( int i = 0; i < noofcases; i ++)
{
long entry = long.Parse(Console.ReadLine());
Console.WriteLine(sieve_of_eratosthenes(entry));
}
}
check the below code. I wrote simple logic which you can improve
public static class Int32Extension
{
public static bool IsPrime(this int number)
{
if (number <= 1) return false;
if (number == 2) return true;
if (number % 2 == 0) return false;
var boundary = (int)Math.Floor(Math.Sqrt(number));
for (int i = 3; i <= boundary; i += 2)
if (number % i == 0)
return false;
return true;
}
}
then
static void Main(string[] args)
{
int input = 5;
int sum = 0;
for (int i = 0; i < input;)
{
if (!(++i).IsPrime())
continue;
sum += i;
}
Console.WriteLine(sum);
}
Without using Extension Method
public static bool IsPrime(int number)
{
if (number <= 1) return false;
if (number == 2) return true;
if (number % 2 == 0) return false;
var boundary = (int)Math.Floor(Math.Sqrt(number));
for (int i = 3; i <= boundary; i += 2)
if (number % i == 0)
return false;
return true;
}
static void Main(string[] args)
{
int input = 5;
int sum = 0;
for (int i = 0; i < input;)
{
if (!IsPrime(++i))
continue;
sum += i;
}
Console.WriteLine(sum);
}
.Net Fiddle Link : https://dotnetfiddle.net/rEBY9r
Edit : The IsPrime test uses Primality Test With Pseudocode
iam try to write min max algorithm for connect4 game but when i run this code it go in infinte loop and not return move or result, so i want a help to know what is wrong with it , i work in board of 6*7 cells
private int score()
{
int x = check_Winner();//i checked it and it work right ot return 1 if player 1 win or 2 if pc win or 0 in tie
if (x == 1) return 10;
else if (x == 2) return -10;
else return 0;
}
public int MinMax(int player_num)
{
List<pair> possiple_moves = get_possible_moves();// it works will
int score_so_far = score();
if (possiple_moves.Count == 0 || score_so_far != 0)
return score_so_far;
List<int> scores = new List<int>();
List<pair> moves = new List<pair>();
foreach (pair move in possiple_moves)
{
if (player_num == 1)
{
cells[move.x, move.y].player_num = 1;
scores.Add(MinMax(2));
}
else
{
cells[move.x, move.y].player_num = 2;
scores.Add(MinMax(1));
}
moves.Add(move);
cells[move.x, move.y].player_num = 0;
}
if (player_num == 1)
{
int max_score_indx = 0, tmp = int.MinValue;
for (int i = 0; i < scores.Count; i++)
{
if (scores[i] > tmp)
{
tmp = scores[i];
max_score_indx = i;
}
}
pc_choise = moves[max_score_indx];
return scores[max_score_indx];
}
//==================
else
{
int min_score_indx = 0, tmp = int.MaxValue;
for (int i = 0; i < scores.Count; i++)
{
if (scores[i] < tmp)
{
tmp = scores[i];
min_score_indx = i;
}
}
pc_choise = moves[min_score_indx];
return scores[min_score_indx];
}
}
#Equalsk is right, I believe: you are calling MinMax from within the foreach loop, before you reach the code that evaluates the stop conditions. So providing that the method get_possible_moves returns something different to Null, you will get stuck in foreach --> MinMax --> foreach --> MinMax --> foreach...
I have coded two C# programs, which use two different approaches to evaluate the outcome of a certain casino-style game (casino-style in the sense that the user pays points to take a turn, and sometimes receives points as a reward depending on that turn’s result). Program 1 calculates the average profitability of the best game play decision for each possible game state, starting at a round's end and working to the beginning. The average profitability of the starting game state is equivalent to, thus can be used to infer, the average profitability of the game as a whole. Program 1 also outputs a proper strategy for the game.
Program 2 accepts a strategy as input (I use the same strategy generated by Program 1), and simulates actual beginning-to-end game play using that strategy, cycling through many iterations of the game to gather statistics. This program outputs the return rate of the game based on the simulated trials (100% being breakeven).
Desired behavior: To produce correct and non-contradictory results in Program 1’s gamePlay.value variable for the starting game state (representing the game’s profitability in points), and Program 2’s returnRate variable (representing the game’s return rate).
Specific problem: Program 1’s gamePlay.value variable for the starting game state (Colorless, Colorless, Colorless) outputs 51.025 when the user inputs the same starting parameters as those which are hard-coded into Program 2 (namely, cost = 51 and baseBet = 50). A secondary task of Program 1 is to calculate the average number of turns remaining in the round, for each possible game state. Again, by noting this value for the starting state, the average number of turns in the round as a whole is known. There are, on average, 4.246 turns per round. By multiplying this number by the cost per turn, 51, we see that the average cost per round is 216.546. Adding the 51.025 profit yields 267.571, and dividing this number by the cost per round reveals a 123.563% return rate for the game.
This is not what Program 2 calculates, even using an extremely large number of game play samples. Some output samples, each of which are the result of one million game play turns, include:
1.00978242220553
1.00976014965254
1.00977590536083
1.0098289475708
1.00979315220468
123.563% and 100.98% are very far from each other.
Code to reproduce problem:
PROGRAM 1
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace theBlindPainterAnalyzerSimplified
{
public partial class Form1 : Form
{
double cost;
double baseBet;
public Form1()
{
InitializeComponent();
}
private void calculateStrategyButton_Click(object sender, EventArgs e) // This is the primary code, analogous to main()
{
cost = Convert.ToDouble(textBox1.Text);
baseBet = Convert.ToDouble(textBox2.Text);
GameState[] gameStates = new GameState[10];
for (int h = 0; h < gameStates.Length; h++)
{
gameStates[h] = new GameState();
}
int i = 0;
foreach (var c in BuildCombinations(Enum.GetValues(typeof(OrderedColors)).Cast<OrderedColors>().Reverse().ToArray(), 3))
{
int j = 0;
foreach (var panel in c)
{
gameStates[i].colors[j] = panel;
j++;
}
i++;
}
foreach (var gameState in gameStates) // Stores the expected value of each decision from each game state
{
int numPreviousStrokes = 0;
for (int j = 0; j < gameState.colors.Length; j++)
{
if (gameState.colors[j] == OrderedColors.Purple)
{
numPreviousStrokes++;
}
else if (gameState.colors[j] == OrderedColors.Blue)
{
numPreviousStrokes += 2;
}
}
double zeroProfitability = 0;
double oneProfitability = checkProfitability(gameState, 1, gameStates);
double twoProfitability = -4;
double threeProfitability = -4;
if (numPreviousStrokes >= 2)
{
twoProfitability = checkProfitability(gameState, 2, gameStates);
}
if (numPreviousStrokes >= 3)
{
threeProfitability = checkProfitability(gameState, 3, gameStates);
}
if (zeroProfitability > oneProfitability && zeroProfitability > twoProfitability && zeroProfitability > threeProfitability)
{
gameState.optimalPlay = 0;
gameState.value = zeroProfitability;
}
else if (oneProfitability > zeroProfitability && oneProfitability > twoProfitability && oneProfitability > threeProfitability)
{
gameState.optimalPlay = 1;
gameState.value = oneProfitability;
}
else if (twoProfitability > zeroProfitability && twoProfitability > oneProfitability && twoProfitability > threeProfitability)
{
gameState.optimalPlay = 2;
gameState.value = twoProfitability;
}
else if (threeProfitability > zeroProfitability && threeProfitability > oneProfitability && threeProfitability > twoProfitability)
{
gameState.optimalPlay = 3;
gameState.value = threeProfitability;
}
else
{
MessageBox.Show("Draw!");
}
gameState.remainingTurnCount = checkRemainingTurnCount(gameState, gameStates);
richTextBox1.Text += gameState.colors[0] + "," + gameState.colors[1] + "," + gameState.colors[2] + "," + gameState.optimalPlay + "," + gameState.value + "," + gameState.remainingTurnCount + "\n";
}
}
private double checkProfitability(GameState state, int numStrokes, GameState[] gameStates) // Calculates the expected value of making a particular decision from a particular game state
{
double[] possiblePayoffs = new double[50000];
int pPIndex = 0;
double sumOfPossiblePayoffs = 0;
double averagePayoff = 0;
double payoff = -cost;
for (int i = 0; i < Math.Pow(3, numStrokes); i++)
{
int innerForIndex = i;
for (int j = 0; j < numStrokes; j++)
{
state.colors[innerForIndex % 3]++;
innerForIndex /= 3;
}
if ((int)state.colors[0] <= 2 && (int)state.colors[1] <= 2 && (int)state.colors[2] <= 2)
{
int numPurple = 0;
int numBlue = 0;
int numNonZeros = 0;
for (int panel = 0; panel <= 2; panel++)
{
if (state.colors[panel] == OrderedColors.Purple)
{
numPurple++;
}
if (state.colors[panel] == OrderedColors.Blue)
{
numBlue++;
}
}
if (numPurple != 0)
{
numNonZeros++;
}
if (numBlue != 0)
{
numNonZeros++;
}
switch (numPurple)
{
case 2:
payoff += (.7 * baseBet);
break;
case 3:
payoff += (2 * baseBet);
break;
}
switch (numBlue)
{
case 2:
payoff += baseBet;
break;
case 3:
payoff += (4.12 * baseBet);
break;
}
if (numPurple + numBlue == 3 && numPurple != 0 && numBlue != 0)
{
payoff += (4 * baseBet);
}
}
OrderedColors[] currentColors = (OrderedColors[])state.colors.Clone(); // Temporary clone of array used to find corrosponding game state
Array.Sort(currentColors);
Array.Reverse(currentColors);
foreach (var gameState in gameStates)
{
if (areArraysEqual(gameState.colors, currentColors))
{
payoff += gameState.value;
break;
}
}
possiblePayoffs[pPIndex] = payoff;
pPIndex++;
payoff = -cost;
innerForIndex = i;
for (int j = 0; j < numStrokes; j++)
{
state.colors[innerForIndex % 3]--;
innerForIndex /= 3;
}
}
for (int i = 0; i <= pPIndex; i++)
{
sumOfPossiblePayoffs += possiblePayoffs[i];
}
averagePayoff = sumOfPossiblePayoffs / pPIndex;
return averagePayoff;
}
private double checkRemainingTurnCount(GameState state, GameState[] gameStates)
{
double[] possibleTurnAverages = new double[50000];
int pTAIndex = 0;
double sumOfPossibleTurnAverages = 0;
double turns = -4;
if (state.optimalPlay == 0)
{
turns = 0;
}
else
{
for (int i = 0; i < Math.Pow(3, state.optimalPlay); i++)
{
int innerForIndex = i;
for (int j = 0; j < state.optimalPlay; j++)
{
state.colors[innerForIndex % 3]++;
innerForIndex /= 3;
}
if ((int)state.colors[0] <= 3 && (int)state.colors[1] <= 3 && (int)state.colors[2] <= 3)
{
OrderedColors[] currentColors = (OrderedColors[])state.colors.Clone(); // Temporary clone of array used to find corrosponding game state
Array.Sort(currentColors);
Array.Reverse(currentColors);
foreach (var gameState in gameStates)
{
if (areArraysEqual(gameState.colors, currentColors))
{
possibleTurnAverages[pTAIndex] = gameState.remainingTurnCount + 1;
pTAIndex++;
break;
}
}
}
else
{
possibleTurnAverages[i] = 1;
pTAIndex++;
}
innerForIndex = i;
for (int j = 0; j < state.optimalPlay; j++)
{
state.colors[innerForIndex % 3]--;
innerForIndex /= 3;
}
}
for (int i = 0; i <= pTAIndex; i++)
{
sumOfPossibleTurnAverages += possibleTurnAverages[i];
}
turns = sumOfPossibleTurnAverages / pTAIndex;
}
return turns;
}
private static IEnumerable<T[]> BuildCombinations<T>(T[] items, int itemsCountInCombination, int startIndex = 0) // Finds all possible unique game states; I did not write this code myself
{
if (itemsCountInCombination == 0)
{
yield return new T[0];
yield break;
}
for (int i = startIndex; i < items.Length; i++)
{
foreach (var combination in BuildCombinations(items, itemsCountInCombination - 1, i))
{
var c = new T[itemsCountInCombination];
c[0] = items[i];
Array.Copy(combination, 0, c, 1, combination.Length);
yield return c;
}
}
}
private bool areArraysEqual<T>(T[] array1, T[] array2)
{
if (Object.ReferenceEquals(array1, array2))
{
return true;
}
if (array1.Length != array2.Length)
{
return false;
}
for (int i = 0; i < array1.Length; i++)
{
if (array1[i].Equals(array2[i]) == false)
{
return false;
}
}
return true;
}
}
public class GameState
{
public OrderedColors[] colors = { OrderedColors.Colorless, OrderedColors.Colorless, OrderedColors.Colorless };
public int optimalPlay = -4; // The value 0 has a specific meaning
public double value = 0;
public double remainingTurnCount;
}
public enum OrderedColors
{
Colorless,
Purple,
Blue
}
}
__
PROGRAM 2
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace theBlindPainterTesterContinuousSimplified
{
public partial class Form1 : Form
{
Random rn1_3 = new Random();
float cost = 51;
float baseBet = 50;
static double startScore = 100000;
static double score = startScore;
int cycledPoints = 0;
double returnRate = 0;
bool isRoundOver = false;
OrderedColors[] panelColors = { OrderedColors.Colorless, OrderedColors.Colorless, OrderedColors.Colorless };
public Form1()
{
InitializeComponent();
}
private void startButton_Click(object sender, EventArgs e)
{
string strategyFileText;
using (StreamReader strategyFile = new StreamReader("strategy.txt"))
{
strategyFileText = strategyFile.ReadToEnd();
}
string[] strats = strategyFileText.Split('\n');
for (int h = 0; h < 1000000; h++)
{
isRoundOver = false;
int selectedPlay = selectPlay(strats);
if (selectedPlay == 0)
{
endRound();
}
else if (selectedPlay >= 1 && selectedPlay <= 3)
{
score = score - cost;
cycledPoints += Convert.ToInt32(cost);
for (int i = 0; i < selectedPlay; i++)
{
if (isRoundOver == false)
{
paintPanel();
}
}
payOut();
}
else
{
MessageBox.Show(selectedPlay + " is not a valid play.");
}
}
if (startScore != 0 && cycledPoints != 0)
{
returnRate = Math.Pow(score / startScore, startScore / cycledPoints);
}
else
{
MessageBox.Show("Division by zero error.");
}
richTextBox1.Text = returnRate.ToString();
}
// Retrieves the appropriate game play for the current panel colors
private int selectPlay(string[] strats)
{
int play = -4; // The value 0 has a specific meaning
Array.Sort(panelColors);
Array.Reverse(panelColors);
foreach (string strat in strats)
{
string[] stratComponents = strat.Split(',');
int matches = 0;
for (int j = 0; j <= 2; j++)
{
if (stratComponents[j] == panelColors[j].ToString())
{
matches++;
}
}
if (matches == 3)
{
play = Convert.ToInt32(stratComponents[3]);
break;
}
}
return play;
}
// Paints a single randomly selected panel
private void paintPanel()
{
int primedPanel = rn1_3.Next(1, 4);
if (panelColors[primedPanel - 1] == OrderedColors.Colorless)
{
panelColors[primedPanel - 1] = OrderedColors.Purple;
}
else if (panelColors[primedPanel - 1] == OrderedColors.Purple)
{
panelColors[primedPanel - 1] = OrderedColors.Blue;
}
else if (panelColors[primedPanel - 1] == OrderedColors.Blue)
{
endRound();
}
}
// Adjusts score and takes action if the game is over
private void payOut()
{
int numPurple = 0;
int numBlue = 0;
int numNonZeros = 0;
for (int panel = 0; panel <= 2; panel++)
{
if (panelColors[panel] == OrderedColors.Purple)
{
numPurple++;
}
if (panelColors[panel] == OrderedColors.Blue)
{
numBlue++;
}
}
if (numPurple != 0)
{
numNonZeros++;
}
if (numBlue != 0)
{
numNonZeros++;
}
switch (numPurple)
{
case 2:
score += (.7 * baseBet);
break;
case 3:
score += (2 * baseBet);
break;
}
switch (numBlue)
{
case 2:
score += baseBet;
break;
case 3:
score += (4.12 * baseBet);
break;
}
if (numPurple + numBlue == 3 && numPurple != 0 && numBlue != 0)
{
score += (4 * baseBet);
}
}
private void endRound()
{
isRoundOver = true;
for (int panel = 0; panel <= 2; panel++)
{
panelColors[panel] = OrderedColors.Colorless;
}
}
}
public enum OrderedColors
{
Colorless,
Purple,
Blue
}
}
__
What I Have Considered: The possible types of problems I have tried to categorize this as are programming logic errors, snowballing rounding errors, and casting errors. It is difficult to isolate the error(s) because Program 1 solves the game using backward induction, while Program 2 gathers information by playing the game start to finish many times, two fundamentally different approaches. I have spent a fairly substantial amount of time with Program 1 in the debugger, including working out the results for the last few game states on paper, and it seems to be functioning properly and to a very respectable precision, as far as I can tell. Program 2 is more difficult to do this with due to the inclusion of randomness in the game, but I have stepped through a small number of iterations and the calculations seem to me to be on point.
Can anyone clarify the reason that these two approaches to return rate calculation produce conflicting results?
The game (this is detailed info about the game being studied, feel free to skip this section): This game consists of three objects called panels, which the user paints. Each panel starts out Colorless. At first, the user must select 1, which paints one random panel. A Colorless panel will turn Purple when painted. A Purple panel will turn Blue when painted again. A Blue panel, when painted again, will immediately cause all panels to return to Colorless, and a new round begins. Once the user has painted a panel, he/she can, on the next turn, select 1 or 2, which will paint one random panel, or two random panels, respectively. Once two units of paint have been applied, the user can select 1, 2, or 3, and the appropriate result is applied. When a new round begins, only option 1 is available until 2 and 3 are unlocked again.
Each turn costs 51 points, regardless of whether 1, 2, or 3 is selected. There is a payout table, as laid out in the code, which may award points back to the user based on the resulting state of the panels. The user can also decide to start a new round at any time, and revert all panels to Colorless, which does not have a cost. The goal of the game is to earn rather than lose points, on average.
Below are my project folders, in case someone wishes to compile, as the interface is not contained in the above code.
http://www.fileconvoy.com/dfl.php?id=geb5da551f035649699958123114d58733569b2fb4