/// <summary> /// Starts the fuego engine and resets its game state to the given state. /// </summary> /// <param name="gameid"></param> /// <param name="state"></param> /// <returns></returns> public async Task <GoGameStateResponse> StartAsync(Guid gameid, GoGameState state) { GoGameStateResponse rval; try { // Either we're starting a new game, or a game already exist and we're just // setting up fuego. Debug.Assert(state != null || _state != null, "state != null || _state != null"); _fuego = new FuegoInstance(); await Task.Factory.StartNew(() => StartProcess(state)); rval = new GoGameStateResponse(GoResultCode.Success, state); } catch (GoEngineException gex) { rval = new GoGameStateResponse(gex.Code, null); } catch (Exception ex) { rval = new GoGameStateResponse(GoResultCode.ServerInternalError, null); } return(rval); }
private void UpdateStateFromExeAndSaveToDatabase() { try { WriteCommand("showboard"); ReadResponse(); var msg = new StringBuilder(); foreach (var l in _debugLines) { } GetStones(); // Gets the new _state.BlackPositions and _state.WhitePositions. if (State.GoMoveHistory == null) { State.GoMoveHistory = new List <GoMoveHistoryItem>(); } // Determine whose turn. State.WhoseTurn = State.WhoseTurn == GoColor.Black ? GoColor.White : GoColor.Black; //switch (move.MoveType) //{ // case MoveType.Resign: // State.Status = move.Color == GoColor.Black // ? GoGameStatus.WhiteWonDueToResignation // : GoGameStatus.BlackWonDueToResignation; // //var gameResult = CalculateGameResult(); // //State.WinMargin = Decimal.Parse(gameResult.Substring(1)); // break; // case MoveType.Pass: // // If previous move was a pass also, calculate winner. // var moveCount = State.GoMoveHistory.Count; // bool previousMoveWasPass = moveCount >= 2 && // State.GoMoveHistory[moveCount - 2].Move.MoveType == MoveType.Pass; // if (previousMoveWasPass) // { // var gameResult2 = CalculateGameResult(); // State.WinMargin = Decimal.Parse(gameResult2.Substring(1)); // State.Status = gameResult2.StartsWith("B") ? GoGameStatus.BlackWon : GoGameStatus.WhiteWon; // } // break; //} //rval.Status = State.Status; //rval.WinMargin = State.WinMargin; // Save to database. _goGRepository.SaveGameState(CurrentGameId, State); //return rval; } catch (DbEntityValidationException) { // Setting State to null will cause it to be loaded from database the next time // a client initiates an operation. State = null; throw; } }
public GoGameState GetGameState(Guid gameId) { using (var ctx = new GoGEntities()) { var g = ctx.Games.FirstOrDefault(game => game.GameId == gameId); if (g == null) { return(null); } var goMoveHistory = new List <GoMoveHistoryItem>(); foreach (var h in g.Moves.OrderBy(h => h.Sequence)) { var newHistoryItem = new GoMoveHistoryItem(); newHistoryItem.Sequence = h.Sequence; // Translate Move from db. newHistoryItem.Move = new GoMove { Color = h.IsBlack ? GoColor.Black : GoColor.White, Position = h.Position, MoveType = (MoveType)h.MoveType }; // Translate Result from db. newHistoryItem.Result = new GoMoveResult(h.Captured); goMoveHistory.Add(newHistoryItem); } var p1 = new GoPlayer { Level = g.Player1Level, Name = g.Player1Name, PlayerType = (PlayerType)g.Player1Type, Score = g.Player1Score, }; var p2 = new GoPlayer { Level = g.Player2Level, Name = g.Player2Name, PlayerType = (PlayerType)g.Player2Type, Score = g.Player2Score }; var rval = new GoGameState(g.Size, p1, p2, (GoGameStatus)g.Status, g.BlacksTurn ? GoColor.Black : GoColor.White, g.BlackStones, g.WhiteStones, goMoveHistory, g.WinMargin); return(rval); } }
//public GoMoveResult Undo() //{ // WriteCommand("undo"); // ReadResponse(); // SaveStones(); // var rval = new GoMoveResult(); // return rval; //} // public void Quit() // { // WriteCommand("quit"); // ReadResponse(); // if (Process != null) // { // try // { // Process.Kill(); // } //// ReSharper disable EmptyGeneralCatchClause // catch //// ReSharper restore EmptyGeneralCatchClause // { // } // } // Process = null; // } //public string Name() //{ // WriteCommand("name"); // string code, msg; // ReadResponse(out code, out msg); // return msg; //} //public string Version() //{ // WriteCommand("version"); // string code, msg; // ReadResponse(out code, out msg); // return msg; //} //public bool IsFunctioning //{ // get // { // try // { // if (Process == null) // return false; // if (!Process.Responding) // return false; // if (Process.HasExited) // return false; // if (Process.ExitTime > Process.StartTime) // return true; // Name(); // if this causes an exception, we're in a bad state // } // catch (Exception) // { // return false; // } // return true; // } //} ///// <summary> ///// Engine must be running or this will throw an error. ///// </summary> //public bool CompareEngineState(GoGameState clientState) //{ // // Get black and white positions from exe. // SaveStones(); // // This does a deep comparison. // var rval = Equals(clientState, _state); // return rval; //} #endregion Public Methods #region Private Helpers private void ReopenGame() { try { State.WinMargin = 0; State.Status = GoGameStatus.Active; // Save to database. _goGRepository.SaveGameState(CurrentGameId, State); } catch (DbEntityValidationException) { // Setting State to null will cause it to be loaded from database the next time // a client initiates an operation. State = null; throw; } }
public void LoadSGF(string sgf) { try { var tmpFilename = Path.GetTempFileName(); File.WriteAllText(tmpFilename, sgf); WriteCommand("loadsgf", '"' + tmpFilename + '"'); ReadResponse(); UpdateStateFromExeAndSaveToDatabase(); } catch (Exception) { // Setting State to null forces it to be loaded from database when the next user request // is made to the service. State = null; throw; } }
public string SaveSGF() { try { var tmpFilename = Path.GetTempFileName(); WriteCommand("savesgf", '"' + tmpFilename + '"'); ReadResponse(); var sgf = File.ReadAllText(tmpFilename, Encoding.UTF8); return(sgf); } catch (Exception) { // Setting State to null forces it to be loaded from database when the next user request // is made to the service. State = null; throw; } }
/// <summary> /// Checks if fuego is running and if not restarts it. If no state, starts the game and sets it up /// to match the database. /// </summary> public void EnsureRunning() { if (Process == null || Process.HasExited) { State = null; RestartProcess(); } if (State == null) { // Try getting state from database. If it doesn't exist in the database, then the user // never started the game (and we probably shouldn't have gotten to this point). If it // does exist in the database, then we must initialize fuego.exe with the database state. var state = _goGRepository.GetGameState(this.CurrentGameId); if (state != null) { Start(state); } } }
public void Start(Guid gameId, GoGameState state) { if (state == null) { throw new ArgumentNullException(nameof(state)); } bool exists = _goGRepository.GetGameExists(gameId); if (exists) { throw new GoEngineException(GoResultCode.GameAlreadyExists, null); } // Get a Fuego instance and start it up. FuegoInstance inst = null; try { inst = GetFuegoInstance(gameId, GoOperation.Starting); inst.Start(state); // Save as the new game state. _goGRepository.SaveGameState(gameId, state); } // ReSharper disable RedundantCatchClause catch (Exception) { throw; } // ReSharper restore RedundantCatchClause finally { if (inst != null) { inst.CurrentOperation = GoOperation.Idle; } } }
public GoGameStateResponse Start(Guid gameid, GoGameState state) { GoGameStateResponse rval; try { // TODO: This code assumes only one player is AI, and uses its level setting. FuegoEngine.Instance.Start(gameid, state); rval = new GoGameStateResponse(GoResultCode.Success, state); } catch (GoEngineException gex) { _logger.LogEngineException(gameid, gex, state); rval = new GoGameStateResponse(gex.Code, null); } catch (Exception ex) { _logger.LogServerError(gameid, ex, state); rval = new GoGameStateResponse(GoResultCode.ServerInternalError, null); } return(rval); }
private void LoadState() { // Note: this is temporary until can figure out why (de)serialization // fails with .Net Native compilation. return; try { if (_sessionStateService.SessionState.ContainsKey(GameStateKey)) { _state = (GoGameState)_sessionStateService.SessionState[GameStateKey]; } //var storageFolder = ApplicationData.Current.LocalFolder; //var file = await storageFolder.GetFileAsync(StateFilename); //var str = await FileIO.ReadTextAsync(file); //_state = JsonConvert.DeserializeObject<GoGameState>(str); } catch (Exception ex) { } }
private async void StartNewGame() { try { bool success = false; GoGameStateResponse resp = null; for (int tries = 0; !AbortOperation && !success && tries < 5; tries++) { BusyMessage = "Starting game..."; IsBusy = true; var tmpNewGame = Guid.NewGuid(); // Create game from user's selections. var p1 = new GoPlayer(); var p2 = new GoPlayer(); if (Color == (int)GoColor.Black) { p1.Name = Name; p1.PlayerType = PlayerType.Human; p2.Name = "Fuego"; p2.PlayerType = PlayerType.AI; p2.Level = DifficultyLevel; } else { p2.Name = Name; p2.PlayerType = PlayerType.Human; p1.Name = "Fuego"; p1.PlayerType = PlayerType.AI; p1.Level = DifficultyLevel; } var tmpState = new GoGameState( tmpNewGame, (byte)BoardEdgeSize, p1, p2, GoGameStatus.Active, GoColor.Black, "", "", new List <GoMoveHistoryItem>(), 0); resp = await DataRepository.StartAsync(tmpNewGame, tmpState); BusyMessage = null; IsBusy = false; ActiveGame = tmpNewGame; success = true; } if (AbortOperation) { return; } if (success) { NavService.Navigate("Game", ActiveGame); } else { if (resp != null) { await DisplayErrorCode(resp.ResultCode); } } } catch (Exception ex) { } finally { BusyMessage = null; IsBusy = false; } }
public GoGameStateResponse(GoResultCode resultCode, GoGameState gameState) : base(resultCode) { GameState = gameState; }
async Task StartProcess(GoGameState state) { if (state != null) { _state = state; } _state.Operation = GoOperation.Starting; SaveState(); _fuego.StartGame(_state.Size); var level = _state.Player1.PlayerType == PlayerType.AI ? _state.Player1.Level : _state.Player2.Level; // Set up parameters and clear board. //await WriteCommand("uct_max_memory", (1024 * 1024 * 250).ToString()); if (level < 3) { ParseResponse(WriteCommand("uct_param_player max_games", ((level + 1) * 10).ToString(CultureInfo.InvariantCulture))); } else if (level < 6) { ParseResponse(WriteCommand("uct_param_player max_games", (level * 2000).ToString(CultureInfo.InvariantCulture))); } else if (level < 9) { ParseResponse(WriteCommand("uct_param_player max_games", (level * 10000).ToString(CultureInfo.InvariantCulture))); } else //if (level < 9) { ParseResponse(WriteCommand("uct_param_player max_games", int.MaxValue.ToString(CultureInfo.InvariantCulture))); } //WriteCommand("komi", state.Komi.ToString(CultureInfo.InvariantCulture)); //ReadResponse(); ParseResponse(WriteCommand("clear_board")); ParseResponse(WriteCommand("go_param_rules", "capture_dead 1")); // Set up board with some pre-existing moves. if (_state.GoMoveHistory.Count > 0) { // Must actually play every move back because otherwise undo operations // won't work. foreach (var m in _state.GoMoveHistory) { string position; switch (m.Move.MoveType) { case MoveType.Normal: position = m.Move.Position; break; case MoveType.Pass: position = "PASS"; break; default: throw new ArgumentException("Unrecognized move type: " + m.Move.MoveType); } ParseResponse(WriteCommand("play", (m.Move.Color == GoColor.Black ? "black" : "white") + ' ' + position)); } } _state.Operation = GoOperation.Idle; SaveState(); }
private GoMoveResult AddMoveAndUpdateStateAndSaveToDatabase(GoMove move) { try { var beforeBlack = State.BlackPositions; var beforeWhite = State.WhitePositions; GetStones(); // Gets the new _state.BlackPositions and _state.WhitePositions. GoMoveResult rval; if (move.Color == GoColor.Black) { rval = new GoMoveResult(beforeWhite, State.WhitePositions); } else { rval = new GoMoveResult(beforeBlack, State.BlackPositions); } if (State.GoMoveHistory == null) { State.GoMoveHistory = new List <GoMoveHistoryItem>(); } State.GoMoveHistory.Add(new GoMoveHistoryItem { Move = move, Result = rval }); // Change turn. State.WhoseTurn = State.WhoseTurn == GoColor.Black ? GoColor.White : GoColor.Black; switch (move.MoveType) { case MoveType.Resign: State.Status = move.Color == GoColor.Black ? GoGameStatus.WhiteWonDueToResignation : GoGameStatus.BlackWonDueToResignation; //var gameResult = CalculateGameResult(); //State.WinMargin = Decimal.Parse(gameResult.Substring(1)); break; case MoveType.Pass: // If previous move was a pass also, calculate winner. var moveCount = State.GoMoveHistory.Count; bool previousMoveWasPass = moveCount >= 2 && State.GoMoveHistory[moveCount - 2].Move.MoveType == MoveType.Pass; if (previousMoveWasPass) { var gameResult2 = CalculateGameResult(); State.WinMargin = Decimal.Parse(gameResult2.Substring(1)); State.Status = gameResult2.StartsWith("B") ? GoGameStatus.BlackWon : GoGameStatus.WhiteWon; } break; } rval.Status = State.Status; rval.WinMargin = State.WinMargin; // Save to database. _goGRepository.SaveGameState(CurrentGameId, State); return(rval); } catch (DbEntityValidationException) { // Setting State to null will cause it to be loaded from database the next time // a client initiates an operation. State = null; throw; } }
private void ContinueGameFromState(GoGameState state) { // Player1 is always black, Player2 is always white. Player1 = new PlayerViewModel(state.Player1, GoColor.Black); Player2 = new PlayerViewModel(state.Player2, GoColor.White); _players = new[] { Player1, Player2 }; WhoseTurn = state.WhoseTurn == GoColor.Black ? 0 : 1; OnPropertyChanged(nameof(WhoseTurn)); CurrentTurnColor = _players[_whoseTurn].Color; SetState(state.Status, state.WinMargin); // Note that setting BoardEdgeSize triggers the board control to generate. BoardEdgeSize = state.Size; // Build a temporary dictionary with ALL the piece states in it, all set to // contain an empty PieceState initially. var tmpPieces = new Dictionary <string, PieceStateViewModel>(); for (int x = 0; x < state.Size; x++) { for (int y = 0; y < state.Size; y++) { var position = EngineHelpers.DecodePosition(x, y, state.Size); tmpPieces.Add(position, new PieceStateViewModel(position, null, null, false, false, false)); } } // This actually updates the UI. Note that we can't add anything to Pieces after // this point because Pieces is a Dictionary, which can't be observed by the GameBoard // control. From here forward, we simply update individual pieces inside Pieces; we // don't add or remove from it. Pieces = tmpPieces; int blackPrisoners = 0; int whitePrisoners = 0; // Save history. History = new ObservableCollection <GoMoveHistoryItem>(); foreach (var h in state.GoMoveHistory) { History.Insert(0, h); if (h.Move.Color == GoColor.Black) { blackPrisoners += h.Result.CapturedStones.Split(' ').Count(x => x != String.Empty); } else { whitePrisoners += h.Result.CapturedStones.Split(' ').Count(x => x != String.Empty); } } Player1.Prisoners = Player1.Color == GoColor.Black ? blackPrisoners : whitePrisoners; Player2.Prisoners = Player2.Color == GoColor.Black ? blackPrisoners : whitePrisoners; // Set piece states for all the existing white and black pieces. SetPieces(state.BlackPositions, GoColor.Black); SetPieces(state.WhitePositions, GoColor.White); var latestNormalPiece = GetLatestNormalMovePieceFromHistory(); if (latestNormalPiece != null) { latestNormalPiece.IsNewPiece = true; } // Correct Sequence value. //FixSequenceValuesForColor(state, GoColor.Black); //FixSequenceValuesForColor(state, GoColor.White); RaiseAllPiecesChanged(); RaiseCommandsChanged(); }
/// <summary> /// Initialize game parameters and board to an given state. /// </summary> public void Start(GoGameState state) { try { if (state == null) { throw new ArgumentNullException("state"); } int level = state.Player1.PlayerType == PlayerType.AI ? state.Player1.Level : state.Player2.Level; State = state; // Set up parameters and clear board. WriteCommand("uct_max_memory", _memoryPerInstance.ToString(CultureInfo.InvariantCulture)); ReadResponse(); WriteCommand("boardsize", state.Size.ToString(CultureInfo.InvariantCulture)); ReadResponse(); if (level < 3) { WriteCommand("uct_param_player max_games", ((level + 1) * 10).ToString(CultureInfo.InvariantCulture)); ReadResponse(); } else if (level < 6) { WriteCommand("uct_param_player max_games", (level * 2000).ToString(CultureInfo.InvariantCulture)); ReadResponse(); } else if (level < 9) { WriteCommand("uct_param_player max_games", (level * 10000).ToString(CultureInfo.InvariantCulture)); ReadResponse(); } else //if (level < 9) { WriteCommand("uct_param_player max_games", Int32.MaxValue.ToString(CultureInfo.InvariantCulture)); ReadResponse(); } //WriteCommand("komi", state.Komi.ToString(CultureInfo.InvariantCulture)); //ReadResponse(); WriteCommand("clear_board"); ReadResponse(); WriteCommand("go_param_rules", "capture_dead 1"); ReadResponse(); //WriteCommand("showboard"); //ReadResponse(); // Set up board with some pre-existing moves. if (state.GoMoveHistory != null && state.GoMoveHistory.Count > 0) { // Must actually play every move back because otherwise undo operations // won't work. foreach (var m in state.GoMoveHistory) { string position; switch (m.Move.MoveType) { case MoveType.Normal: position = m.Move.Position; break; case MoveType.Pass: position = "PASS"; break; default: throw new ArgumentException("Unrecognized move type: " + m.Move.MoveType); } WriteCommand("play", (m.Move.Color == GoColor.Black ? "black" : "white") + ' ' + position); ReadResponse(); } //var param = new StringBuilder((19 * 19) * 9); //if (state.BlackPositions != null) //{ // foreach (var p in state.BlackPositions.Trim().Split(' ')) // { // if (!String.IsNullOrEmpty(p)) // { // param.Append(" black "); // param.Append(p); // } // } //} //if (state.WhitePositions != null) //{ // foreach (var p in state.WhitePositions.Trim().Split(' ')) // { // if (!String.IsNullOrEmpty(p)) // { // param.Append(" white "); // param.Append(p); // } // } //} //WriteCommand("gogui-setup", param.ToString().Trim()); //ReadResponse(); } } catch (Exception) { State = null; // Forces next attempt to reload state again. throw; } }
public void SaveGameState(Guid gameId, GoGameState gameState) { var newMoveHistory = gameState.GoMoveHistory; using (var ctx = new GoGEntities()) { // locate game by GameId var g = ctx.Games.FirstOrDefault(game => game.GameId == gameId); if (g == null) { // add game var newGame = new Game { BlackStones = gameState.BlackPositions, BlacksTurn = gameState.WhoseTurn == GoColor.Black, GameId = gameId, Size = gameState.Size, WhiteStones = gameState.WhitePositions, Started = DateTimeOffset.Now, LastMove = DateTimeOffset.Now, Player1Level = (byte)gameState.Player1.Level, Player2Level = (byte)gameState.Player2.Level, Player1Name = gameState.Player1.Name, Player2Name = gameState.Player2.Name, Player1Score = gameState.Player1.Score, Player2Score = gameState.Player2.Score, Player1Type = (byte)gameState.Player1.PlayerType, Player2Type = (byte)gameState.Player2.PlayerType, Status = (byte)gameState.Status, WinMargin = gameState.WinMargin }; UpdateMoves(ctx, newGame, newMoveHistory); ctx.Games.Add(newGame); } else { // update game g.BlackStones = gameState.BlackPositions; g.BlacksTurn = gameState.WhoseTurn == GoColor.Black; g.GameId = gameId; g.Size = gameState.Size; g.WhiteStones = gameState.WhitePositions; g.LastMove = DateTimeOffset.Now; g.Player1Level = (byte)gameState.Player1.Level; g.Player2Level = (byte)gameState.Player2.Level; g.Player1Name = gameState.Player1.Name; g.Player2Name = gameState.Player2.Name; g.Player1Score = gameState.Player1.Score; g.Player2Score = gameState.Player2.Score; g.Player1Type = (byte)gameState.Player1.PlayerType; g.Player2Type = (byte)gameState.Player2.PlayerType; g.Status = (byte)gameState.Status; g.WinMargin = gameState.WinMargin; UpdateMoves(ctx, g, newMoveHistory); } ctx.SaveChanges(); } }
public void Undo() { try { // Note that resignation is stored as a single move, but fuego.exe doesn't know about resignations so // no need to send an undo command to the engine. int undo = 0; if (State.Status == GoGameStatus.BlackWonDueToResignation) { var humanColor = State.Player1.PlayerType == PlayerType.Human ? GoColor.Black : GoColor.White; undo = humanColor == GoColor.Black ? 2 : 1; if (State.GoMoveHistory.Count > 1 && State.GoMoveHistory[State.GoMoveHistory.Count - 2].Move.Color == humanColor) { WriteCommand("gg-undo", "1"); ReadResponse(); } } else if (State.Status == GoGameStatus.WhiteWonDueToResignation) { var humanColor = State.Player1.PlayerType == PlayerType.Human ? GoColor.Black : GoColor.White; undo = humanColor == GoColor.White ? 2 : 1; if (State.GoMoveHistory.Count > 1 && State.GoMoveHistory[State.GoMoveHistory.Count - 2].Move.Color == humanColor) { WriteCommand("gg-undo", "1"); ReadResponse(); } } else { var his = State.GoMoveHistory; var count = his.Count; var humanColor = State.Player1.PlayerType == PlayerType.Human ? GoColor.Black : GoColor.White; // Reverse to before most recent human move. for (int i = count - 1; i >= 0; i--) { if (his[i].Move.Color == humanColor) { undo = count - i; break; } } if (undo == 0) { throw new Exception("Can't undo because there are no human moves yet."); } WriteCommand("gg-undo", undo.ToString(CultureInfo.InvariantCulture)); ReadResponse(); } UndoMovesInStateAndDatabase(undo); } catch (Exception) { // Setting State to null forces it to be loaded from database when the next user request // is made to the service. State = null; throw; } }