C# 当两个不同的程序输出时,为什么一个游戏的回报率会有所不同?
我编写了两个C#程序,它们使用两种不同的方法来评估某个赌场式游戏的结果(赌场式游戏是指用户为回合支付点数,有时根据回合结果获得点数作为奖励)。程序1计算每个可能的游戏状态的最佳游戏决策的平均盈利能力,从一轮结束到开始。开始游戏状态的平均盈利能力相当于,因此可以用来推断整个游戏的平均盈利能力。程序1还为游戏输出适当的策略 程序2接受一个策略作为输入(我使用程序1生成的相同策略),并使用该策略模拟实际的从开始到结束的游戏,在游戏的多次迭代中循环以收集统计数据。该程序根据模拟试验输出游戏的回报率(100%为盈亏平衡) 所需行为:在程序1的gamePlay.value变量(以点数表示游戏的盈利能力)和程序2的returnRate变量(以游戏的回报率表示)中生成正确且不矛盾的结果 具体问题:当用户输入与硬编码到程序2中的启动参数相同的启动参数时,程序1的gamePlay.value变量(无色、无色、无色)的启动游戏状态输出51.025(即,成本=51,基本赌注=50)。程序1的第二个任务是计算每一个可能的游戏状态在回合中剩余的平均回合数。同样,通过记录起始状态的该值,可以知道整轮的平均圈数。平均每轮4.246圈。通过将这个数字乘以每回合的成本51,我们可以看到每轮的平均成本是216.546。加上51.025的利润得到267.571,再除以每轮的成本,游戏的回报率为123.563% 这不是程序2计算的结果,即使使用了大量的游戏示例。一些输出示例(每个示例都是100万次游戏回合的结果)包括: 1.00978242220553 1.00976014965254 1.00977590536083 1.0098289475708 1.00979315220468 123.563%和100.98%相差甚远 重现问题的代码: 方案1C# 当两个不同的程序输出时,为什么一个游戏的回报率会有所不同?,c#,winforms,debugging,logic,simulation,C#,Winforms,Debugging,Logic,Simulation,我编写了两个C#程序,它们使用两种不同的方法来评估某个赌场式游戏的结果(赌场式游戏是指用户为回合支付点数,有时根据回合结果获得点数作为奖励)。程序1计算每个可能的游戏状态的最佳游戏决策的平均盈利能力,从一轮结束到开始。开始游戏状态的平均盈利能力相当于,因此可以用来推断整个游戏的平均盈利能力。程序1还为游戏输出适当的策略 程序2接受一个策略作为输入(我使用程序1生成的相同策略),并使用该策略模拟实际的从开始到结束的游戏,在游戏的多次迭代中循环以收集统计数据。该程序根据模拟试验输出游戏的回报率(10
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
}
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统数据;
使用系统图;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows.Forms;
对Blind Painter和Analyzer的名称空间进行了简化
{
公共部分类Form1:Form
{
双重成本;
双基投注;
公共表格1()
{
初始化组件();
}
private void calculateStrategyButton_Click(objectsender,EventArgs e)//这是主代码,类似于main()
{
成本=Convert.ToDouble(textBox1.Text);
baseBet=Convert.ToDouble(textBox2.Text);
游戏状态[]游戏状态=新游戏状态[10];
对于(inth=0;h=2)
{
Two盈利能力=检查盈利能力(游戏状态,2,游戏状态);
}
如果(numPreviousStrokes>=3)
{
三个盈利能力=检查盈利能力(博弈状态,3,博弈状态);
}
if(零盈利能力>一盈利能力和零盈利能力>两盈利能力和零盈利能力>三盈利能力)
{
gameState.optimalPlay=0;
gameState.value=零盈利能力;
}
否则如果(一个盈利能力>零盈利能力和一个盈利能力>两个盈利能力和一个盈利能力>三个盈利能力)
{
gameState.optimalPlay=1;
gameState.value=1;
}
否则如果(两个盈利能力>零盈利能力和两个盈利能力>一个盈利能力和两个盈利能力>三个盈利能力)
{
gameState.optimalPlay=2;
gameState.value=two盈利能力;
}
否则如果(三个盈利能力>零盈利能力和三个盈利能力>一个盈利能力和三个盈利能力>两个盈利能力)
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
}
}