-
-
Save tylorahn/877d336a3a1344757463 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace GameProject | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var game = new TwoPlayerZeroSumGame(); | |
Console.WriteLine(game.GetPayoffMatrix()); | |
Console.ReadLine(); | |
} | |
} | |
/// <summary> | |
/// I have tried to make the class applicable for more general games (to be specific, two player zero-sum game). | |
/// This perhaps added unnecessary details, but who knows if I might need to do similar computation in future. | |
/// For instance, for this particular Kuhn poker game, PlayerType is not really useful. | |
/// </summary> | |
public class TwoPlayerZeroSumGame | |
{ | |
/// <summary> | |
/// Returns the Player 1's payoff matrix in string format. | |
/// </summary> | |
public string GetPayoffMatrix() | |
{ | |
string matrix = "Player1Matrix,"; | |
var player1Strategies = GetAllPlayer1Strategies(); | |
var player2Strategies = GetAllPlayer2Strategies(); | |
// Add a header. | |
foreach (var player2Strategy in player2Strategies) | |
matrix += player2Strategy.ToString() + ","; | |
matrix += Environment.NewLine; | |
// Add each row. | |
foreach (var player1Strategy in player1Strategies) | |
{ | |
var row = player1Strategy.ToString() + ","; | |
foreach (var player2Strategy in player2Strategies) | |
row += GetExpectedUtility(player1Strategy, player2Strategy).ToString("F2") + ","; | |
matrix += row + Environment.NewLine; | |
} | |
return matrix; | |
} | |
public List<Strategy> GetAllPlayer1Strategies() | |
{ | |
var strategies = new List<Strategy>(); | |
var firstMoves = new List<char>() { 'C', 'B' }; | |
var secondMoves = new List<char>() { 'F', 'C' }; | |
foreach (var firstJ in firstMoves) | |
foreach (var firstQ in firstMoves) | |
foreach (var firstK in firstMoves) | |
foreach (var secondJ in secondMoves) | |
foreach (var secondQ in secondMoves) | |
foreach (var secondK in secondMoves) | |
{ | |
var notation = | |
string.Format("{0}{1}{2}{3}{4}{5}", | |
firstJ, firstQ, firstK, secondJ, secondQ, secondK); | |
// If Player 1 plays B in the first round, he has no option on the second round(X). | |
for (int i = 0; i < 3; i++) | |
if (notation[i] == 'B') | |
notation = Utility.ReplaceAt(notation, i + 3, 'X'); | |
var strategy = new Strategy(PlayerType.Player1, notation); | |
if (!strategies.Contains(strategy)) | |
strategies.Add(strategy); | |
} | |
return strategies; | |
} | |
public List<Strategy> GetAllPlayer2Strategies() | |
{ | |
var strategies = new List<Strategy>(); | |
var firstMoves = new List<char>() { 'F', 'C' }; | |
var secondMoves = new List<char>() { 'C', 'B' }; | |
foreach (var firstJ in firstMoves) | |
foreach (var firstQ in firstMoves) | |
foreach (var firstK in firstMoves) | |
foreach (var secondJ in secondMoves) | |
foreach (var secondQ in secondMoves) | |
foreach (var secondK in secondMoves) | |
{ | |
var notation = | |
string.Format("{0}{1}{2}{3}{4}{5}", | |
firstJ, firstQ, firstK, secondJ, secondQ, secondK); | |
var strategy = new Strategy(PlayerType.Player2, notation); | |
strategies.Add(strategy); | |
} | |
return strategies; | |
} | |
/// <summary> | |
/// Returns the list of all states. | |
/// </summary> | |
public List<string> GetAllStates() | |
{ | |
return new List<string>() { "KQ", "KJ", "QK", "QJ", "JK", "JQ" }; | |
} | |
/// <summary> | |
/// Returns the probability that a given state is realized. | |
/// In Kuhn poker game, every state is always realized with the probability of 1/6. | |
/// </summary> | |
public double StateProbability(string state) | |
{ | |
return (1d / 6); | |
} | |
/// <summary> | |
/// Returns the expected utility of player 1. | |
/// </summary> | |
public double GetExpectedUtility(Strategy myStrategy, Strategy otherStrategy) | |
{ | |
double expectedUtility = 0; | |
var states = GetAllStates(); | |
foreach (var state in states) | |
expectedUtility += StateProbability(state) * GetUtility(myStrategy, otherStrategy, state); | |
return expectedUtility; | |
} | |
/// <summary> | |
/// Returns the utility of player 1 according to the rule of a game. | |
/// </summary> | |
public double GetUtility(Strategy myStrategy, Strategy otherStrategy, string state) | |
{ | |
if ((myStrategy.Response(state[0]))[0] == 'B') | |
if ((otherStrategy.Response(state[1]))[0] == 'F') | |
return 1; | |
else | |
if (Player1Wins(state[0], state[1])) | |
return 2; | |
else | |
return -2; | |
else | |
if ((otherStrategy.Response(state[1]))[1] == 'C') | |
if (Player1Wins(state[0], state[1])) | |
return 1; | |
else | |
return -1; | |
else | |
if ((myStrategy.Response(state[0]))[1] == 'F') | |
return -1; | |
else | |
if (Player1Wins(state[0], state[1])) | |
return 2; | |
else | |
return -2; | |
} | |
/// <summary> | |
/// Returns true if player 1 wins. | |
/// </summary> | |
public static bool Player1Wins(char player1State, char player2State) | |
{ | |
return (StateToNum(player1State) > StateToNum(player2State)); | |
} | |
/// <summary> | |
/// Returns a number according to state. J -> 0, Q -> 1, K -> 2. | |
/// </summary> | |
public static int StateToNum(char state) | |
{ | |
switch (state) | |
{ | |
case 'J': | |
return 0; | |
case 'Q': | |
return 1; | |
case 'K': | |
return 2; | |
default: | |
return -1; | |
} | |
} | |
} | |
public class Strategy | |
{ | |
public readonly PlayerType PlayerType; | |
public readonly string Notation; | |
public Strategy(PlayerType playerType, string notation) | |
{ | |
PlayerType = playerType; | |
Notation = notation; | |
} | |
/// <summary> | |
/// Returns response to the current state according to a strategy. | |
/// For this Kuhn poker game, if a strategy is CBCFXC and a given state is J, returns "CF" | |
/// where first letter represents his response on the first stage, the latter for the second stage. | |
/// </summary> | |
public string Response(char myState) | |
{ | |
var stateNum = TwoPlayerZeroSumGame.StateToNum(myState); | |
var first = Notation[stateNum]; | |
var second = Notation[stateNum + 3]; | |
return string.Format("{0}{1}", first, second); | |
} | |
/// <summary> | |
/// Two strategies are equal if their notations are identical. | |
/// </summary> | |
public override bool Equals(object obj) | |
{ | |
Strategy other = obj as Strategy; | |
if (obj == null) | |
return false; | |
return Notation == other.Notation; | |
} | |
public override int GetHashCode() | |
{ | |
return this.Notation.GetHashCode(); | |
} | |
public override string ToString() | |
{ | |
return Notation; | |
} | |
} | |
public enum PlayerType | |
{ | |
Player1, | |
Player2, | |
} | |
public static class Utility | |
{ | |
public static string ReplaceAt(this string input, int index, char newChar) | |
{ | |
char[] chars = input.ToCharArray(); | |
chars[index] = newChar; | |
return new string(chars); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment