public static GameState GnuBGIDToGameState(string gnubg_id, ref string error)
{
GameState gs = new GameState(GameType.Match);
error = "";
if (!gnubg_id.Contains(':') || gnubg_id.Length != 27) // 14 (position id) + 12 (match id) + 1 (':')
{
error = "Invalid GNUBG ID.";
return null;
}
string[] tmp = gnubg_id.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (tmp.Length != 2)
return null;
string pos_id = tmp[0];
string match_id = tmp[1];
if (match_id.Length != 12)
{
if (match_id.Length < 12)
error = "Match ID length too short.";
else
error = "Match ID length too long.";
return null;
}
Board board = BoardFromPositionID(pos_id, ref error);
if (board == null)
return null;
gs.Board = board;
List<bool> bits2 = new List<bool>();
List<bool> bits = new List<bool>();
foreach (char c in match_id)
{
if (!base64.Contains(c))
{
error = "Match ID contains an invalid character.";
return null;
}
int dec = base64.IndexOf(c);
for (int i = 5; i >= 0; i--)
{
bits.Add((powers2[i] & dec) == powers2[i]);
bool bit = (powers2[i] & dec) == powers2[i];
Console.Write(bit ? 1 : 0);
if (bits.Count == 8)
{
bits.Reverse();
bits2.AddRange(bits);
bits.Clear();
}
}
}
// 1-4 cube
int cube = BoolListToInt(bits2.Take(4).Reverse());
if (cube > 15)
{
error = "Invalid cube value.";
return null;
}
// 5-6 cube owner
int cube_owner = BoolListToInt(bits2.Skip(4).Take(2).Reverse());
if (cube_owner == 3)
gs.CenterCube();
else
gs.SetCube((int)Math.Pow(2, cube), cube_owner);
// 7 player on roll
gs.PlayerOnRoll = bits2[6] ? 1 : 0;
// 8 crawford
gs.IsCrawford = bits2[7];
// TODO, check if moneygame and this is set, return null
// 9-11 game state, 000 for no game started, 001 for playing a game, 010 if the game is over, 011 if the game was resigned, or 100 if the game was ended by dropping a cube.
int game_state = BoolListToInt(bits2.Skip(8).Take(2).Reverse());
if (game_state < 0 || game_state > 4)
{
error = "Invalid game state in Match ID.";
return null;
}
// Do nothing with game state for now.
// TODO: handle game_state this or not?
// 12 player on turn
gs.PlayerOnTurn = bits2[11] ? 1 : 0;
// 13 double offered
bool double_offered = bits2[12];
// 14-15 resignation offered
int resign_value = BoolListToInt(bits2.Skip(13).Take(2).Reverse());
if (double_offered && resign_value > 0)
{
error = "Cannot offer double and resign at the same time.";
return null;
}
int[] dice = new int[2];
// 16-18 first die
dice[0] = BoolListToInt(bits2.Skip(15).Take(3).Reverse());
// 19-21 first die
dice[1] = BoolListToInt(bits2.Skip(18).Take(3).Reverse());
if ((dice[0] == 0 && dice[1] != 0) || (dice[1] == 0 && dice[0] != 0) || dice[0] > 6 || dice[1] > 6)
{
error = "Invalid dice.";
return null;
}
if (dice[0] > 0 && double_offered)
{
error = "Cannot offer double when dice have been thrown.";
return null;
}
gs.SetDice(dice[0], dice[1]);
if (double_offered)
{
gs.OfferType = OfferType.Double;
gs.SetCube(2 * gs.Cube.Value, gs.Cube.Owner); // This is because GameState was designed so that on double offer, the cube value stored in gamestate is the actual offer value. In contrast, Gnubg stores the non-doubled value.
}
if (resign_value > 0)
{
gs.OfferType = OfferType.Resign;
gs.ResignOfferValue = (ResignValue)resign_value;
}
// 22-36 match length, zero indicates a money game
int match_length = BoolListToInt(bits2.Skip(21).Take(15).Reverse());
// TODO: What do we do with money game, ie. match length = 0?
gs.MatchTo = match_length;
// 37-51 player 0 score
int score0 = BoolListToInt(bits2.Skip(36).Take(15).Reverse());
// 52-66 player 1 score
int score1 = BoolListToInt(bits2.Skip(51).Take(15).Reverse());
gs.SetScore(score0, score1);
/*Console.WriteLine();
foreach (bool bit in bits2)
Console.Write(bit ? 1 : 0);
Console.WriteLine();*/
//Console.WriteLine(gs);
return gs;
}