public EvaluationInfo Eval(GameState gamestate) { //Set2PlyEvaluation(); SetGameState(gamestate); string s = Command("eval"); int start = s.IndexOf("static:") + "static:".Length;//"2 ply:") + "2 ply:".Length; int end = s.IndexOf('\r', start); s = s.Substring(start, end - start); string[] t = s.Trim().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); EvaluationInfo eval_info = new EvaluationInfo() { Win = double.Parse(t[0]), WinGammon = double.Parse(t[1]), WinBackgammon = double.Parse(t[2]), LoseGammon = double.Parse(t[3]), LoseBackgammon = double.Parse(t[4]) }; eval_info.Lose = 1.0 - eval_info.Win; //Set0PlyEvaluation(0.005); return eval_info; }
public DoubleHint DoubleHint(GameState gamestate) { return GetDoubleHint(gamestate);//new DoubleHint() { Action = ParseDoubleAction(Hint(gamestate)) }; }
public DoubleResponseHint DoubleResponseHint(GameState gamestate) { return ParseDoubleResponse(Hint(gamestate)); }
// total_hints = the number of parseble move hints in the gnubg string, ie. 6/off contains one, bar/23 6/3 contains two private Move ParseMoveHint(string move_string, out int count, int total_hints, int[] dice, GameState gamestate) { // from/to // from/to(count) // 'bar'/to // 'bar'/to(count) // from/'off' // from/'off'(count) // from/to*/to* // from/to*/to*/to* // from/'off' // get count and remove it from the string count = 1; int count_start_index = move_string.IndexOf('('); if (count_start_index > 0) { int count_end_index = move_string.IndexOf(')'); count = int.Parse(move_string.Substring(count_start_index + 1, count_end_index - count_start_index - 1)); move_string = move_string.Substring(0, count_start_index); } string[] point_strings = move_string.Split(new char[] { '/' }); List<int> points = new List<int>(); HashSet<int> hitpoints = new HashSet<int>(); bool is_number; foreach (string point_string in point_strings) { bool hit = point_string.Contains("*"); string clean_point_string = point_string; if (hit) clean_point_string = clean_point_string.Replace("*", ""); is_number = true; foreach (char c in clean_point_string) if (!char.IsDigit(c)) { is_number = false; break; } if (is_number) { int point = int.Parse(clean_point_string) - 1; points.Add(point); if (hit) hitpoints.Add(point); } } if (point_strings[0][0] == 'b') points.Insert(0, 24); if (point_strings[point_strings.Length - 1][0] == 'o') points.Insert(points.Count, -1); // points should be sorted from highest to lowest at this point // calculate the missing way points if (dice[0] != dice[1]) { if (count == 1 && points.Count == 2) { int distance = points[0] - points[1]; int bigger_die = Math.Max(dice[0], dice[1]); int smaller_die = Math.Min(dice[0], dice[1]); if (distance > bigger_die)//(points[0] - points[1]) == (dice[0] + dice[1])) { if (distance == (dice[0] + dice[1])) { // Must not contain hits, because gnubg tells about the hit points seperately. if (gamestate.Board.PointCount(gamestate.PlayerOnRoll, points[0] - bigger_die) >= 0) points.Insert(0, (points[0] - bigger_die)); else points.Insert(0, (points[0] - smaller_die)); } else if (points[0] - bigger_die < smaller_die) // bearoff from 6 with 52, need first to do the smaller one. { if (gamestate.Board.PointCount(gamestate.PlayerOnRoll, points[0] - smaller_die) >= 0) // Was broken without this condition on _ _ 8x 2o 2o 2o with dice 43 points.Insert(0, (points[0] - smaller_die)); else if (gamestate.Board.PointCount(gamestate.PlayerOnRoll, points[0] - smaller_die) == -1) points.Insert(0, (points[0] - bigger_die)); else { } } else { throw new InvalidOperationException("Cannot handle this. Help!"); } /*if (gamestate.Board.PointCount(gamestate.PlayerOnRoll, points[0] - bigger_die) > -2) points.Insert(0, (points[0] - bigger_die)); else points.Insert(0, (points[0] - smaller_die));*/ } // This is for lonely bearoffs like |_ _ x _ x x| with 41, gnubg gives one of the hints as '4/off'. // We want atleast 2 chequers on the field because otherwise adding a waypoint is unnecessary inbetween. else if (total_hints == 1 && gamestate.Board.FinishedCount(gamestate.PlayerOnRoll) <= 13 && points[1] == -1 && (points[0] - smaller_die >= 0) && gamestate.Board.PointCount(gamestate.PlayerOnRoll, points[0] - smaller_die) >= 0 && gamestate.Board.LastChequer(gamestate.PlayerOnRoll) <= points[0]) { points.Insert(0, (points[0] - smaller_die)); } else { } } } else { for (int i = 0; i < points.Count - 1; i++) { // This was broken with bearoffs without this condition (ie. 6/off with 44, the below else condition should handle this situation) /*if (points[i + 1] > -1) {*/ int gaps = (points[i] - points[i + 1]) / dice[0]; int mod = (points[i] - points[i + 1]) % dice[0]; if (mod > 0) gaps++; if (gaps > 1) { for (int c = 1; c < gaps; c++) { points.Insert(i + 1, points[i] - dice[0]); i++; } } /*} else { int gaps = (points[i] - points[i + 1]) / dice[0]; int mod = (points[i] - points[i + 1]) % dice[0]; if (mod > 0) { for (int c = 0; c < gaps; c++) { points.Insert(i + 1, points[i] - dice[0]); i++; } } }*/ } } Move move = new Move(); foreach (int point in points) { if (hitpoints.Contains(point)) move.AddHitPoint(point); else move.AddPoint(point); } return move; }
// TODO, unfinished. // See drawboard.c to see how FIBS ID handling is done in gnubg. // http://www.fibs.com/fibs_interface.html#board_state public static GameState FromFIBS(string id, ref string error) { GameState gs = new GameState(GameType.Match); error = ""; string[] s = id.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (s.Length != 53) return null; if (s[0] != "board") return null; //string[] player_names = new string[] { s[1], s[2] }; gs.SetName(0, s[1]); gs.SetName(1, s[2]); int match_length = 0; if (!int.TryParse(s[3], out match_length)) { error = "FIBS ID parsing error: Incorrect match length."; return null; } gs.MatchTo = match_length; int player0score, player1score; if (!int.TryParse(s[4], out player0score) || !int.TryParse(s[5], out player1score)) { error = "FIBS ID parsing error: Invalid match score."; return null; } gs.SetScore(player0score, player1score); int count; int[] total_counts = new int[2]; for (int p = 0; p < 26; p++) { if (!int.TryParse(s[6 + p], out count)) { error = "FIBS ID parsing error: Invalid board."; return null; } if (p == 0) { if (count > 0) return null; gs.Board.SetCaptured(1, -count); total_counts[1] += -count; } if (p == 25) { if (count < 0) return null; gs.Board.SetCaptured(0, count); total_counts[0] += count; } if (p >= 1 && p <= 24 && count != 0) { gs.Board.SetPoint(0, 6 + p - 1, count); total_counts[count > 0 ? 0 : 1] += Math.Abs(count); } } int turn; if (!int.TryParse(s[32], out turn)) return null; gs.PlayerOnTurn = (turn == -1) ? 0 : 1; int[] dice = new int[2]; if (!int.TryParse(s[33], out dice[0]) && !int.TryParse(s[34], out dice[1])) return null; if (dice[0] > 0 && dice[1] > 0) gs.SetDice(dice[0], dice[1]); int cube_value; if (!int.TryParse(s[35], out cube_value)) return null; return gs; }
public PlayHint PlayHint(GameState gamestate) { List<PlayHint> play_hints = PlayHint(gamestate, 1); if (play_hints == null) return null; return play_hints[0]; }
/// <summary> /// Doesn't use the match id for setting up the position. /// </summary> /// <param name="gamestate"></param> public void SetGameState(GameState gamestate) { bool set_cube = true; if (gamestate.GameType == GameType.Money) { int wager = gamestate.Stake * gamestate.Cube.Value; bool capped = (wager >= gamestate.Limit); // Evaluate as a single point match if the game is capped or the cube is centered (jacoby rule) and leave the cube at the center. if (gamestate.DiceRolled && (capped || gamestate.Cube.Centered)) { // Console.WriteLine("EVALUATING AS A MATCH"); Command("new match 1"); set_cube = false; } else { int max_points = (int)(gamestate.Limit / gamestate.Stake); if (max_points * gamestate.Stake < gamestate.Limit) max_points++; if (max_points < 1) max_points = 1; if (gamestate.Stake < gamestate.Limit && (met_stake != gamestate.Stake || met_limit != gamestate.Limit)) { // Console.WriteLine("Setting money MET."); MatchEquityTable.CreateRakelessMet("gnubg/met.xml", gamestate.Stake, gamestate.Limit); Command("set met met.xml"); met_stake = gamestate.Stake; met_limit = gamestate.Limit; } Command("new match " + max_points); } } else if (gamestate.GameType == GameType.Match) // What is only set in matches. { Command("new match " + gamestate.MatchTo); Command("set score " + gamestate.Score(0) + " " + gamestate.Score(1)); // The score (after 0 games) is: gnubg 2, Administrator 2 (match to 3 points, post-Crawford play). // Need to be 1-away from match length to be able to set crawford game. if (gamestate.MatchTo - gamestate.Score(0) == 1 || gamestate.MatchTo - gamestate.Score(1) == 1) Command("set crawford " + (gamestate.IsCrawford ? "true" : "false")); // Cannot set Crawford play for money sessions. // This game is the Crawford game (no doubling allowed). } else { } // This also clears the dice! Command("set turn " + gamestate.PlayerOnRoll); SetBoardSimple(gamestate); if (gamestate.OfferType == OfferType.Double) { Command("set turn " + gamestate.Cube.Owner); Command("set cube owner " + gamestate.Cube.Owner);// gamestate.PlayerOnRoll); Command("set cube value " + (gamestate.Cube.Value / 2)); Command("double"); } else if (gamestate.OfferType == OfferType.Resign) { if (gamestate.ResignOfferValue == ResignValue.None) Console.WriteLine("Resign offer but ResignValue == None!"); Command("resign " + (int)gamestate.ResignOfferValue); } else { if (gamestate.DiceRolled) Command("set dice " + gamestate.Dice[0] + " " + gamestate.Dice[1]); // The dice have been set to 6 and 6. if (gamestate.Cube.Centered || !set_cube) { Command("set cube center"); // The cube has been centred. } else { Command("set cube owner " + gamestate.Cube.Owner); // gnubg now owns the cube. Command("set cube value " + gamestate.Cube.Value); // The cube has been set to 2. } } }
public static Bitmap Render(int player, int width, int height, GameState gamestate, ref List<BoundingChequer> bounding_chequers) { bounding_chequers.Clear(); int[] board = gamestate.Board.BoardRelativeTo(player); int field_width = (int)(0.30 * width); int bar_width = (int)(0.08 * width); int left_space = (int)(0.16 * width); int right_space = (int)(0.16 * width); int field_height = (int)(0.92 * height); int upper_space = (int)(0.04 * height); int lower_space = (int)(0.04 * height); int chequer_diameter = (int)(field_width / 6.0); int chequer_radius = (int)(chequer_diameter / 2.0); int die_width = chequer_diameter; int slot_width = chequer_diameter; Bitmap bitmap = new Bitmap(width, height); Graphics g = Graphics.FromImage(bitmap); g.Clear(Color.LightGray); Brush[] player_brushes = new Brush[] { Brushes.Brown, Brushes.White }; int[] slots = new int[24] { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; int base_x = left_space; int base_y = upper_space; int base_y_down = height - lower_space - chequer_diameter; int x = base_x; int count = -1, y = -1; Brush brush; FontFamily fm = new FontFamily("Tahoma"); Font font = new Font(fm, 17, FontStyle.Bold); // Cube float cube_y = gamestate.Cube.Centered ? ((float)(height / 2.0 - die_width / 2.0)) : (gamestate.Cube.Owner == 1 ? upper_space : (height - lower_space - die_width)); g.DrawImageUnscaled(GameStateRenderer.CreateDie(gamestate.Cube.Value, die_width, Brushes.Black, Brushes.White), (int)(left_space / 2.0 - die_width / 2.0), (int)cube_y); // Finished counts string finished_text = gamestate.Board.FinishedCount(1).ToString(); SizeF text_size = g.MeasureString(finished_text, font); if (gamestate.Board.FinishedCount(1) > 0) g.DrawString(finished_text, font, Brushes.White, new PointF((float)(width - right_space / 2.0 - text_size.Width / 2.0), base_y)); finished_text = gamestate.Board.FinishedCount(0).ToString(); text_size = g.MeasureString(finished_text, font); if (gamestate.Board.FinishedCount(0) > 0) g.DrawString(finished_text, font, Brushes.Brown, new PointF((float)(width - right_space / 2.0 - text_size.Width / 2.0), (float)(height - lower_space - text_size.Height))); for (int s = 0; s < 12; s++) { int tri_x = x - ((slot_width / 2) - chequer_radius); int tri_y_down = upper_space + (int)(6 * chequer_diameter); int try_y_down2 = height - lower_space;//left_space + (int)(5.5 * chequer_diameter) + (int)(5.5 * chequer_diameter); Brush triangle_brush = (s % 2 == 0) ? Brushes.DarkGray : Brushes.White; g.FillPolygon(triangle_brush, new Point[] { new Point(tri_x, base_y), new Point(tri_x + slot_width, base_y), new Point(tri_x + slot_width / 2, base_y + 5 * chequer_diameter) }); g.FillPolygon(triangle_brush, new Point[] { new Point(tri_x, try_y_down2 + 1), new Point(tri_x + slot_width / 2, tri_y_down), new Point(tri_x + slot_width, try_y_down2 + 1) }); /*string text = (slots[s] + 1).ToString(); SizeF size = g.MeasureString(text, font); float text_x = (float)(x + chequer_radius) - size.Width / 2.0f; g.DrawString(text, font, Brushes.Blue, new PointF(text_x, left_space - size.Height)); text = (slots[12 + s] + 1).ToString(); size = g.MeasureString(text, font); text_x = (float)(x + chequer_radius) - size.Width / 2.0f; g.DrawString(text, font, Brushes.Blue, new PointF(text_x, try_y_down2 + 2));*/ // render up count = board[slots[s]]; y = base_y; //int player = (count > 0) ? 0 : 1;//board[slots[s]].Player; brush = player_brushes[(count > 0) ? 0 : 1]; bool hero = (count > 0) ? true : false; if (count < 0) count *= -1; for (int c = 0; c < count; c++) { if (c % 5 == 0) y = base_y + chequer_radius; if (c % 10 == 0) y = base_y; g.FillEllipse(brush, x, y, chequer_diameter, chequer_diameter); g.DrawEllipse(Pens.Black, x, y, chequer_diameter, chequer_diameter); if (hero) bounding_chequers.Add(new BoundingChequer(new Rectangle(x, y, chequer_diameter, chequer_diameter), slots[s])); y += chequer_diameter; } // render down count = board[slots[12 + s]]; y = base_y_down; //player = (count > 0) ? 0 : 1; brush = player_brushes[(count > 0) ? 0 : 1]; hero = (count > 0) ? true : false; if (count < 0) count *= -1; for (int c = 0; c < count; c++) { if (c % 5 == 0) y = base_y_down - chequer_radius; if (c % 10 == 0) y = base_y_down; g.FillEllipse(brush, x, y, chequer_diameter, chequer_diameter); g.DrawEllipse(Pens.Black, x, y, chequer_diameter, chequer_diameter); if (hero) bounding_chequers.Add(new BoundingChequer(new Rectangle(x, y, chequer_diameter, chequer_diameter), slots[12 + s])); y -= chequer_diameter; } x += slot_width; if (s == 5) x = left_space + 6 * slot_width + bar_width + (slot_width / 2) - chequer_radius; } // Captured x = (int)(width / 2.0) - chequer_radius; // Player's count = gamestate.Board.CapturedCount(player); y = base_y + 4 * chequer_diameter; brush = player_brushes[0]; for (int c = 0; c < count; c++) { if (c % 5 == 0) y = base_y + 4 * chequer_diameter - chequer_radius; if (c % 10 == 0) y = base_y + 4 * chequer_diameter; g.FillEllipse(brush, x, y, chequer_diameter, chequer_diameter); g.DrawEllipse(Pens.Black, x, y, chequer_diameter, chequer_diameter); bounding_chequers.Add(new BoundingChequer(new Rectangle(x, y, chequer_diameter, chequer_diameter), 24)); y -= chequer_diameter; } // Opponent's count = gamestate.Board.CapturedCount(1 - player); y = height - lower_space - 5 * chequer_diameter; brush = player_brushes[1]; for (int c = 0; c < count; c++) { if (c % 5 == 0) y = height - lower_space - 5 * chequer_diameter + chequer_radius; if (c % 10 == 0) y = height - lower_space - 5 * chequer_diameter; g.FillEllipse(brush, x, y, chequer_diameter, chequer_diameter); g.DrawEllipse(Pens.Black, x, y, chequer_diameter, chequer_diameter); // bounding_chequers.Add(new BoundingChequer(new Rectangle(x, y, chequer_diameter, chequer_diameter), 25)); y += chequer_diameter; } // Dice /*SizeF size; string text; int[] dice = gamestate.Dice; if (dice[0] != 0) { text = dice[0].ToString() + "," + dice[1].ToString(); size = g.MeasureString(text, font); g.DrawString(text, font, Brushes.Blue, new PointF(left_space + 3 * slot_width - size.Width / 2, height / 2 - size.Height / 2)); }*/ if (gamestate.DiceRolled) { int[] dice_x = new int[2]; if (player == gamestate.PlayerOnRoll) { dice_x[0] = (int)(left_space + field_width * 0.25 - die_width * 0.5); dice_x[1] = (int)(left_space + field_width * 0.75 - die_width * 0.5); } else { dice_x[0] = (int)(left_space + field_width + bar_width + field_width * 0.25 - die_width * 0.5); dice_x[1] = (int)(left_space + field_width + bar_width + field_width * 0.75 - die_width * 0.5); } g.DrawImageUnscaled(CreateDie(gamestate.Dice[0], die_width, player_brushes[gamestate.PlayerOnRoll], player_brushes[1 - gamestate.PlayerOnRoll]), dice_x[0], (int)(height * 0.5 - die_width * 0.5)); g.DrawImageUnscaled(CreateDie(gamestate.Dice[1], die_width, player_brushes[gamestate.PlayerOnRoll], player_brushes[1 - gamestate.PlayerOnRoll]), dice_x[1], (int)(height * 0.5 - die_width * 0.5)); } // Outer borders g.DrawRectangle(Pens.Black, 0, 0, width - 1, height - 1); // Fields g.DrawRectangle(Pens.Black, left_space, upper_space, field_width, field_height); // Left g.DrawRectangle(Pens.Black, width - right_space - field_width, upper_space, field_width, field_height); // Right return bitmap; }
private void buttonRoll_Click(object sender, EventArgs e) { watch.Stop(); GameStateTurnAction gsta = new GameStateTurnAction(currentGameState.Clone(), watch.ElapsedMilliseconds, TurnAction.Roll); turns.Add(gsta); textBoxLog.Text += "Turn action added" + " " + watch.ElapsedMilliseconds + "ms." + Environment.NewLine; textBoxLog.SelectionStart = textBoxLog.Text.Length; textBoxLog.ScrollToCaret(); buttonRoll.Enabled = false; buttonRoll.Visible = false; currentGameState.SetDice(random.Next(1, 7), random.Next(1, 7)); originalGameState = currentGameState.Clone(); legalPlays = currentGameState.Board.LegalPlays(currentGameState.PlayerOnRoll, currentGameState.Dice); if (legalPlays.Count == 0) { this.Render(); this.Refresh(); legalPlays.Clear(); madeMoves.Clear(); unusedDice.Clear(); currentGameState.ChangeTurn(); Thread.Sleep(1000); UpdateControls(); return; } unusedDice.AddRange(currentGameState.Dice); if (currentGameState.Dice[0] == currentGameState.Dice[1]) unusedDice.AddRange(currentGameState.Dice); UpdateControls(); }
/// <summary> /// Compares this to another game state and determines if they are the same. /// </summary> /// <param name="state"></param> /// <returns></returns> public bool Equals(GameState state) { if (this.player_on_roll != state.PlayerOnRoll) return false; if (this.PlayerOnTurn != state.PlayerOnTurn) return false; if (this.Dice[0] != state.Dice[0] || this.Dice[1] != state.Dice[1]) return false; if (this.Cube.Owner != state.Cube.Owner || this.Cube.Value != state.Cube.Value) return false; if (this.ResignOfferValue != state.ResignOfferValue) return false; return this.Board.Equals(state.Board); }
public static Bitmap Render(GameState gamestate) { Board board = gamestate.Board; int cheq_rad = 6; int slot_width = 18, bar_width = 15, border_width = 20, middle_height = 8; int cheq_diam = cheq_rad * 2; int total_width = 2 * border_width + 12 * slot_width + bar_width + 1; int total_height = 2 * border_width + 2 * (int)(5.5 * cheq_diam) + middle_height + 1; Bitmap render = new Bitmap(total_width, total_height); //int[] slots = new int[24] {0,1,2,3,4,5,6,7,8,9,10,11,23,22,21,20,19,18,17,16,15,14,13,12}; int[] slots = new int[24] { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; int base_x = border_width + (slot_width / 2) - cheq_rad; int base_y = border_width; int base_y_down = border_width + (int)(5.5 * cheq_diam) + middle_height + (int)(4.5 * cheq_diam); Graphics g = Graphics.FromImage(render); g.Clear(Color.LightGray); g.DrawRectangle(Pens.Turquoise, 0, 0, total_width - 1, total_height - 1); g.DrawRectangle(Pens.DarkGray, border_width - 1, border_width - 1, slot_width * 6 + 2, cheq_diam * 11 + middle_height + 2); g.DrawRectangle(Pens.DarkGray, border_width + slot_width * 6 + bar_width - 1, border_width - 1, slot_width * 6 + 2, cheq_diam * 11 + middle_height + 2); Dictionary<int, Brush> player2brush = new Dictionary<int, Brush>(); player2brush[0] = Brushes.Brown; player2brush[1] = Brushes.White; int x = base_x; FontFamily fm = new FontFamily("Tahoma"); Font font = new Font(fm, 7, FontStyle.Bold); // cube string cube_text = gamestate.Cube.Value.ToString(); SizeF text_size = g.MeasureString(cube_text, font); g.DrawString(cube_text, font, Brushes.Blue, new PointF(border_width / 2 - text_size.Width / 2, total_height / 2 - text_size.Height / 2)); // finished counts /*string finished_text = "C:{" + gamestate.Board.CapturedCount(0) + ", " + gamestate.Board.CapturedCount(1) + "}"; finished_text += " F:{" + gamestate.Board.FinishedCount(0) + ", " + gamestate.Board.FinishedCount(1) + "}"; SizeF text_size = g.MeasureString(finished_text, font);*/ /* for (int s = 0; s < 12; s++) { int tri_x = x - ((slot_width / 2) - cheq_rad); int tri_y_down = border_width + (int)(6 * cheq_diam) + middle_height; int try_y_down2 = border_width + (int)(5.5 * cheq_diam) + middle_height + (int)(5.5 * cheq_diam); g.FillPolygon(Brushes.LightGoldenrodYellow, new Point[] { new Point(tri_x, base_y), new Point(tri_x + slot_width, base_y), new Point(tri_x + slot_width / 2, base_y + 5 * cheq_diam) }); g.FillPolygon(Brushes.LightGoldenrodYellow, new Point[] { new Point(tri_x, try_y_down2 + 1), new Point(tri_x + slot_width / 2, tri_y_down), new Point(tri_x + slot_width, try_y_down2 + 1) }); string text = (slots[s] + 1).ToString(); SizeF size = g.MeasureString(text, font); float text_x = (float)(x + cheq_rad) - size.Width / 2.0f; g.DrawString(text, font, Brushes.Blue, new PointF(text_x, border_width - size.Height)); text = (slots[12 + s] + 1).ToString(); size = g.MeasureString(text, font); text_x = (float)(x + cheq_rad) - size.Width / 2.0f; g.DrawString(text, font, Brushes.Blue, new PointF(text_x, try_y_down2 + 2)); int[] dice = gamestate.Dice; if (dice[0] != 0) { text = dice[0].ToString() + "," + dice[1].ToString(); size = g.MeasureString(text, font); g.DrawString(text, font, Brushes.Blue, new PointF(border_width + 3 * slot_width - size.Width / 2, total_height / 2 - size.Height / 2)); } // render up int count = board.Count(slots[s]); int y = base_y; int player = board[slots[s]].Player; for (int c = 0; c < count; c++) { if (c % 5 == 0) y = base_y + cheq_rad; if (c % 10 == 0) y = base_y; g.FillEllipse(player2brush[player], x, y, cheq_diam, cheq_diam); g.DrawEllipse(Pens.Black, x, y, cheq_diam, cheq_diam); y += cheq_diam; } // render down count = board.Count(slots[12 + s]); y = base_y_down; player = board[slots[12 + s]].Player; for (int c = 0; c < count; c++) { if (c % 5 == 0) y = base_y_down - cheq_rad; if (c % 10 == 0) y = base_y_down; g.FillEllipse(player2brush[player], x, y, cheq_diam, cheq_diam); g.DrawEllipse(Pens.Black, x, y, cheq_diam, cheq_diam); y -= cheq_diam; } x += slot_width; if (s == 5) x = border_width + 6 * slot_width + bar_width + (slot_width / 2) - cheq_rad; } */ return render; }
public GameState Clone() { GameState gamestate = new GameState(this.game_type); gamestate.board = this.board.Clone(); gamestate.crawford = this.crawford; gamestate.cube.Owner = this.cube.Owner; gamestate.cube.Value = this.cube.Value; gamestate.dice[0] = this.dice[0]; gamestate.dice[1] = this.dice[1]; gamestate.limit = this.limit; gamestate.match_to = this.match_to; gamestate.offer = this.offer; gamestate.player_on_roll = this.player_on_roll; gamestate.player_on_turn = this.player_on_turn; gamestate.resign_offer_value = this.resign_offer_value; gamestate.score[0] = this.score[0]; gamestate.score[1] = this.score[1]; gamestate.stake = this.stake; gamestate.names = this.names; return gamestate; }
public static string Serialize(GameState gs) { StringBuilder sb = new StringBuilder(); sb.Append(gs.crawford.ToString()); sb.Append(" " + gs.cube.Owner); sb.Append(" " + gs.cube.Value); sb.Append(" " + gs.dice[0]); sb.Append(" " + gs.dice[1]); sb.Append(" " + gs.game_type.ToString()); sb.Append(" " + gs.limit); sb.Append(" " + gs.match_to); sb.Append(" " + gs.offer.ToString()); sb.Append(" " + gs.player_on_roll); sb.Append(" " + gs.player_on_turn); sb.Append(" " + gs.resign_offer_value); sb.Append(" " + gs.score[0]); sb.Append(" " + gs.score[1]); sb.Append(" " + gs.stake); sb.Append("#" + Board.Serialize(gs.board)); return sb.ToString(); }
public static GameState Deserialize(string s) { string[] ss = s.Split(new char[] { '#' }, StringSplitOptions.RemoveEmptyEntries); GameState gs = new GameState(GameType.Match); gs.board = Board.Deserialize(ss[1]); ss = ss[0].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int i=0; gs.crawford = bool.Parse(ss[i]); i++; gs.cube.Owner = int.Parse(ss[i]); i++; gs.cube.Value = int.Parse(ss[i]); i++; gs.dice[0] = int.Parse(ss[i]); i++; gs.dice[1] = int.Parse(ss[i]); i++; gs.game_type = (GameType)Enum.Parse(typeof(GameType), ss[i]); i++; gs.limit = int.Parse(ss[i]); i++; gs.match_to = int.Parse(ss[i]); i++; gs.offer = (OfferType)Enum.Parse(typeof(OfferType), ss[i]); i++; gs.player_on_roll = int.Parse(ss[i]); i++; gs.player_on_turn = int.Parse(ss[i]); i++; gs.resign_offer_value = (ResignValue)Enum.Parse(typeof(ResignValue), ss[i]); i++; gs.score[0] = int.Parse(ss[i]); i++; gs.score[1] = int.Parse(ss[i]); i++; gs.stake = int.Parse(ss[i]); return gs; }
public string Hint(GameState gamestate) { // Command("new match " + gamestate.MatchTo); // SetBoardSimple(gamestate); // string matchid_response = Command("set matchid " + MatchID(gamestate)); SetGameState(gamestate); // TODO: Current results give no difference with or without clear hint, without it, there might be performance boost. Investigate further. //Command("clear hint"); return Command("hint"); }
private void StartNew() { currentGameState = new GameState(GameType.Money); currentGameState.Stake = 1; currentGameState.Limit = 8; currentGameState.Board.InitializeBoard(BackgammonVariation.Standard); currentGameState.PlayerOnRoll = 0; currentGameState.PlayerOnTurn = 0; int[] startRoll = new int[2] { 0, 0 }; while (startRoll[0] == startRoll[1]) { startRoll[0] = random.Next(1, 7); startRoll[1] = random.Next(1, 7); } currentGameState.SetDice(startRoll[0], startRoll[1]); currentGameState.PlayerOnRoll = currentGameState.PlayerOnTurn = random.Next(2); SetTurn(currentGameState.PlayerOnTurn); if (currentGameState.PlayerOnTurn == 1) HandleAI(); else { unusedDice.AddRange(currentGameState.Dice); if (currentGameState.Dice[0] == currentGameState.Dice[1]) unusedDice.AddRange(currentGameState.Dice); legalPlays = currentGameState.Board.LegalPlays(currentGameState.PlayerOnRoll, currentGameState.Dice); originalGameState = currentGameState.Clone(); } UpdateControls(); }
/// <summary> /// Assumes dice have been rolled and game is still going. /// </summary> /// <param name="state"></param> /// <returns>Returns null if there are no legal moves.</returns> public List<PlayHint> PlayHint(GameState gamestate, int max_hints) { SetGameState(gamestate); // TODO: Current results give no difference with or without clear hint, without it, there might be performance boost. Investigate further. //Command("clear hint"); return ParseMoveHints(Command("hint " + max_hints), gamestate.Dice, gamestate); }
/// <summary> /// Gnubg treats positive counts as for the player on turn, and negative for the other. /// The first slot is the captured count for the player on turn, last slot is for the capture count of the other. /// Gnubg calculates the finished pieces for both players automatically. /// 26 integers are represented the following way: /// #Captured count for the player on roll# #Slot 1# ... #Slot 24# #Captured count for the other player# /// The slots start from the home board of the player on roll. /// </summary> /// <param name="game_state"></param> private void SetBoardSimple(GameState gamestate) { string simple_board = ""; simple_board += gamestate.Board.CapturedCount(gamestate.PlayerOnRoll); int[] board = gamestate.Board.BoardRelativeTo(gamestate.PlayerOnRoll); for (int i = 0; i < 24; i++) simple_board += " " + board[i].ToString(); simple_board += " " + gamestate.Board.CapturedCount(1 - gamestate.PlayerOnRoll); //Console.WriteLine(simple_board); //Console.WriteLine(Command("set board simple " + simple_board)); Command("set board simple " + simple_board); }
public ResignResponseHint ResignResponseHint(GameState gamestate) { return ParseResignResponse(Hint(gamestate)); }
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; }
DoubleHint GetDoubleHint(GameState gamestate) { string hint = Hint(gamestate); Console.WriteLine(gamestate); int start = hint.IndexOf("No double") + 9; int end = hint.IndexOf("\r\n", start); string h = hint.Substring(start, end - start); if (h.Contains("(")) h = h.Substring(0, h.IndexOf("(")); double noDoubleEq = double.Parse(h.Trim()); start = hint.IndexOf("Double, pass") + 12; end = hint.IndexOf("\r\n", start); h = hint.Substring(start, end - start); if (h.Contains("(")) h = h.Substring(0, h.IndexOf("(")); double doubePassEq = double.Parse(h.Trim()); start = hint.IndexOf("Double, take") + 12; end = hint.IndexOf("\r\n", start); h = hint.Substring(start, end - start); if (h.Contains("(")) h = h.Substring(0, h.IndexOf("(")); double doubeTakeEq = double.Parse(h.Trim()); start = hint.LastIndexOf(":"); string sub = hint.Substring(start + 1); if (sub.Contains("(")) sub = sub.Remove(sub.LastIndexOf("(")); if (sub.Contains("\r\n\r\n" + this.prompt)) sub = sub.Remove(sub.LastIndexOf("\r\n\r\n" + this.prompt)); if (sub.Contains(",")) sub = sub.Remove(sub.IndexOf(",")); sub = sub.Trim(); sub = sub.ToLowerInvariant(); // We should double when the cube is centered and it's a money game. if ((gamestate.GameType == GameType.Money && gamestate.Cube.Centered) && (sub == "too good to double" || sub == "too good to redouble")) { return new DoubleHint(DoubleAction.Double, doubeTakeEq, doubePassEq, noDoubleEq); } return new DoubleHint(string2doubleaction[sub], doubeTakeEq, doubePassEq, noDoubleEq); }
/// <summary> /// TODO: Solve the "I'm sorry, but SetMatchID cannot handle positions where a double has been offered" bullshit. /// </summary> /// <param name="gamestate"></param> /// <returns></returns> public static string MatchID(GameState gamestate) { Console.WriteLine("Cube owner: " + gamestate.Cube.Owner); return MatchID( gamestate.Dice[0], gamestate.Dice[1], gamestate.PlayerOnTurn, (int)gamestate.ResignOfferValue, (gamestate.OfferType == OfferType.Double) ? 1 : 0, gamestate.PlayerOnRoll, (gamestate.Cube.Owner >= 0) ? gamestate.Cube.Owner : 2, gamestate.IsCrawford ? 1 : 0, gamestate.MatchTo, gamestate.Score(0), gamestate.Score(1), gamestate.Cube.Value, (int)GnuBGGameState.GAME_PLAYING ); }
private List<PlayHint> ParseMoveHints(string hints, int[] dice, GameState gamestate) { if (hints.StartsWith("There are no legal moves.")) return null; List<PlayHint> move_hints = new List<PlayHint>(); //string[] hs = hints.Split(new char[] { ']' }); string[] hs = hints.Split(new string[] { ". " }, StringSplitOptions.RemoveEmptyEntries); string hint = ""; // Skip the first one. for (int i = 1; i < hs.Length; i++) { hint = hs[i]; int s = hint.IndexOf("-ply"); if (s < 0) continue; int e = hint.IndexOf("Eq.:"); if (e < 0) { e = hint.IndexOf("MWC"); } if (e < 0) { e = hint.IndexOf("uity"); } // Parse equity. int eq_e = hint.IndexOfAny(new char[] { '(', '\r' }, e + 4); double eq = double.Parse(hint.Substring(e + 4, eq_e - (e + 4))); hint = hint.Substring(s + 4, e - s - 4).Trim(); // Console.WriteLine("GnuBg: " + hint); string[] moves_strings = hint.Split(new char[] { ' ' }); int count = 0; List<Move> moves = new List<Move>(); foreach (string move_string in moves_strings) { Move move = ParseMoveHint(move_string, out count, moves_strings.Length, dice, gamestate); for (int c = 0; c < count; c++) moves.Add(move); } PlayHint play_hint = new PlayHint(moves, eq); play_hint.Play.SortHiToLow(); move_hints.Add(play_hint); } return move_hints; }
// See drawboard.c in Gnubg source files. public static string ToGnuBgASCII(GameState gs, string[] player_names) { string[] a = new string[7]; string achX = " X6789ABCDEF"; string achO = " O6789ABCDEF"; a[0] = string.Format("O: {0}", player_names[0]); a[6] = string.Format("X: {0}", player_names[1]); if (gs.Score(0) == 1) a[1] = string.Format("{0} point", gs.Score(0)); else a[1] = string.Format("{0} points", gs.Score(0)); if (gs.Score(1) == 1) a[5] = string.Format("{0} point", gs.Score(1)); else a[5] = string.Format("{0} points", gs.Score(1)); if (gs.OfferType == OfferType.Double) { a[(gs.PlayerOnTurn == 1) ? 4 : 2] = string.Format("Cube offered at {0}", gs.Cube.Value); } else { int index = (gs.PlayerOnRoll == 1) ? 4 : 2; if (gs.DiceRolled) a[index] += string.Format("Rolled {0}{1}", gs.Dice[0], gs.Dice[1]); else if (!gs.HasOffer) a[index] = "On roll"; else { } if (gs.Cube.Centered) { if (gs.GameType == GameType.Match) a[3] += string.Format("{0} point match (Cube: {1})", gs.MatchTo, gs.Cube.Value); else a[3] += string.Format("(Cube: {0})", gs.Cube.Value); } else { a[(gs.Cube.Owner == 1) ? 6 : 0] = string.Format("{0}: {1} (Cube: {2})", (gs.Cube.Owner == 1) ? "X" : "O", "CubeOwnersName", gs.Cube.Value); if (gs.GameType == GameType.Match) a[3] += string.Format("{0} point match", gs.MatchTo); } } if (gs.OfferType == OfferType.Resign) { string[] resign_strigns = new string[] { "single game", "gammon", "backgammon" }; a[(gs.PlayerOnRoll == 1) ? 4 : 2] += string.Format(", resigns {0}", resign_strigns[(int)gs.ResignOfferValue - 1]); } Board b = gs.Board; Console.WriteLine(b.CapturedCount(0) + " " + b.CapturedCount(1)); if (gs.PlayerOnRoll == 0) b = SwapSides(b); string s = ""; s += string.Format(" {0,-15} {1}: ", "GNU Backgammon", "Position ID"); s += "4HPwATDg6+ABMA";//GnuBg.ToPositionID(gs.Board, gs.PlayerOnRoll); s += Environment.NewLine; s += string.Format(" {0} : {1}" + Environment.NewLine, "Match ID", "MQHgAAAACAAA"); //GnuBg.ToMatchID(gs); s += (gs.PlayerOnRoll == 1) ? " +13-14-15-16-17-18------19-20-21-22-23-24-+ " : " +12-11-10--9--8--7-------6--5--4--3--2--1-+ "; s += a[0] + Environment.NewLine; Console.WriteLine(b.CapturedCount(0) + " " + b.CapturedCount(1)); int x = 0, y = 0; for (y = 0; y < 4; y++) { s += " "; s += "|"; for (x = 12; x < 18; x++) { s += " "; s += (b.PointCount(1, x) > y) ? "X" : (b.PointCount(0, 23 - x) > y) ? "O" : " "; // X or O or ' ' TODO! s += " "; } s += "|"; s += " "; s += b.CapturedCount(0) > y ? "O" : " "; // O or ' ' TODO! s += " "; s += "|"; for (; x < 24; x++) { s += " "; s += (b.PointCount(1, x) > y) ? "X" : (b.PointCount(0, 23 - x) > y) ? "O" : " "; // X or O or ' ' TODO! s += " "; } s += "|"; s += " "; for (x = 0; x < 3; x++) s += b.FinishedCount(0) > (5 * x + y) ? "O" : " "; // O or ' ' TODO! s += " "; if (y < 2 && a[y + 1] != "") { s += a[y + 1]; } s += Environment.NewLine; } s += " "; s += "|"; for (x = 12; x < 18; x++) { s += " "; s += b.PointCount(1, x) > 0 ? achX[b.PointCount(1, x)] : achO[b.PointCount(0, 23 - x)];// TODO, WTF? s += " "; } s += "|"; s += " "; s += achO[b.CapturedCount(0)]; // TODO! s += " "; s += "|"; for (; x < 24; x++) { s += " "; s += b.PointCount(1, x) > 0 ? achX[b.PointCount(1, x)] : achO[b.PointCount(0, 23 - x)]; // TODO! s += " "; } s += "|"; s += " "; for (x = 0; x < 3; x++) s += b.FinishedCount(0) > (5 * x + 4) ? "O" : " "; ; // TODO! s += Environment.NewLine; s += (gs.PlayerOnRoll == 1) ? "v" : "^"; s += "| |BAR| | "; s += a[3]; s += Environment.NewLine; s += " "; s += "|"; for (x = 11; x > 5; x--) { s += " "; s += b.PointCount(1, x) > 0 ? achX[b.PointCount(1, x)] : achO[b.PointCount(0, 23 - x)]; // TODO! s += " "; } s += "|"; s += " "; s += achX[b.CapturedCount(1)]; // TODO! s += " "; s += "|"; for (; x >= 0; x--) { s += " "; s += b.PointCount(1, x) > 0 ? achX[b.PointCount(1, x)] : achO[b.PointCount(0, 23 - x)]; // TODO! s += " "; } s += "|"; s += " "; for (x = 0; x < 3; x++) s += b.FinishedCount(1) > (5 * x + 4) ? "X" : " "; // TODO! s += Environment.NewLine; for (y = 3; y >= 0; y--) { s += " "; s += "|"; for (x = 11; x > 5; x--) { s += " "; s += b.PointCount(1, x) > y ? "X" : b.PointCount(0, 23 - x) > y ? "O" : " "; // TODO! s += " "; } s += "|"; s += " "; s += b.CapturedCount(1) > y ? "X" : " "; // TODO! s += " "; s += "|"; for (; x >= 0; x--) { s += " "; s += b.PointCount(1, x) > y ? "X" : b.PointCount(0, 23 - x) > y ? "O" : " "; // TODO! s += " "; } s += "|"; s += " "; for (x = 0; x < 3; x++) s += b.FinishedCount(1) > (5 * x + y) ? "X" : " "; // TODO! s += " "; if (y < 2) s += a[5 - y]; s += Environment.NewLine; } s += (gs.PlayerOnRoll == 1) ? " +12-11-10--9--8--7-------6--5--4--3--2--1-+ " : " +13-14-15-16-17-18------19-20-21-22-23-24-+ "; s += a[6]; s += Environment.NewLine; Console.WriteLine(s); return s; }