private void BackgroundWorkCompleted(object sender, RunWorkerCompletedEventArgs e) { if (!e.Cancelled) { _lastSearch = (SearchResult)e.Result; if (_lastSearch.BestMove >= 0 && CurrentState.IsValidMove(_lastSearch.BestMove)) { bool growthHappened = CurrentState.MakeMove(_lastSearch.BestMove); MoveHistory.Add(_lastSearch.BestMove); if (growthHappened) { MoveHistory.Add(Constants.AllGrowMove); } OnMoveMade?.Invoke(growthHappened); if (CurrentState.State == GameState.GameOver) { OnGameOver?.Invoke(CurrentState.Winner, CurrentState.Winner == Player.Draw ? VictoryType.InfiniteEruption : VictoryType.AntipodePathCreation); } else { ComputerPlay(); } } else { // If an engine doesn't find a move, then he adjourns the game CurrentState.Winner = CurrentState.Player == Player.One ? Player.Two : Player.One; OnGameOver?.Invoke(CurrentState.Winner, VictoryType.ArenaAdjudication); } } }
public void Move(Direction direction) { LastMove = new Move(State, direction); MoveHistory.Add(LastMove); State = LastMove.EndState; ULongMagic.SetRandomSquare(ref State, rnd); }
public void UndoLastMove() { TicTacToeMove m = MoveHistory.Last() as TicTacToeMove; SetPosition(m.Position, 0); MoveHistory.RemoveAt(MoveHistory.Count - 1); }
private void UndoButton_Click(object sender, RoutedEventArgs e) { if (MoveHistory.Count < 1) { return; } Untrack(); Move lastMove = MoveHistory.Last(); Cell target = BoardGrid.GetCellAt(lastMove.Target); target.Filled = false; Cell source = BoardGrid.GetCellAt(lastMove.Source); Cell middle = BoardGrid.GetMiddleCell(source, target); middle.Selected = false; middle.Filled = true; source.Selected = false; source.Filled = true; UndoHistory.Add(lastMove); MoveHistory.RemoveAt(MoveHistory.Count - 1); }
public string GetTranscript(bool includeHeader) { if (MoveHistory.Count > 0) { StringBuilder sb = new StringBuilder(); if (includeHeader) { sb.AppendLine("[Volcanoes Saved Game Transcript v1]"); sb.AppendLine("[Date " + DateTime.Now.ToString("yyyy.MM.dd") + "]"); sb.AppendLine(""); } if (Settings.IndicateTranscriptMoveType) { var moves = GetDetailedMoveList(); sb.AppendLine(moves.Select(x => x.Tile + (x.Addition ? "+" : "")).Aggregate((c, n) => c + " " + n)); } else { sb.AppendLine(MoveHistory.Select(x => Constants.TileNames[x]).Aggregate((c, n) => c + " " + n)); } return(sb.ToString()); } return(""); }
private void InitUI() { // Initialize Root Object _gameUI = new UIContainer(new Vector2f(GlobalConstants.BoardLength * GlobalConstants.SquareSize, 0), new Vector2f(GlobalConstants.WindowWidth - GlobalConstants.BoardLength * GlobalConstants.SquareSize, GlobalConstants.SquareSize * GlobalConstants.BoardLength), new Color(215, 215, 215)); // Move History moveHistory = new MoveHistory(new Vector2f(_gameUI.Position.X, _gameUI.Position.Y + GlobalConstants.SquareSize), new Vector2f(_gameUI.Shape.Size.X, _gameUI.Shape.Size.Y - (GlobalConstants.SquareSize * 2))); // Quit Button GraphicButton quitButton = new GraphicButton( new Vector2f(_gameUI.Position.X + _gameUI.Shape.Size.X - ButtonWidth, _gameUI.Position.Y + _gameUI.Shape.Size.Y - ButtonHeight), new Vector2f(ButtonWidth, ButtonHeight), new Color(100, 100, 100), new Color(150, 150, 150)); quitButton.SetGraphics(Application.Instance().AssetManager.Textures[TextureID.ReturnIdle], Application.Instance().AssetManager.Textures[TextureID.ReturnHover]); quitButton.SetCommand(new GoToStateCommand(new MenuState())); quitButton.SetBorder(1, Color.Black); // New Game Button GraphicButton newGameButton = new GraphicButton( new Vector2f(_gameUI.Position.X + _gameUI.Shape.Size.X - (ButtonWidth * 2), _gameUI.Position.Y + _gameUI.Shape.Size.Y - ButtonHeight), new Vector2f(ButtonWidth, ButtonHeight), new Color(100, 100, 100), new Color(150, 150, 150)); newGameButton.SetGraphics(Application.Instance().AssetManager.Textures[TextureID.NewGameIdle], Application.Instance().AssetManager.Textures[TextureID.NewGameHover]); newGameButton.SetCommand(new ResetGameCommand(this)); newGameButton.SetBorder(1, Color.Black); // Add Elements _gameUI.AddElement(moveHistory); _gameUI.AddElement(quitButton); _gameUI.AddElement(newGameButton); }
protected override bool RecordServiceHistory() { MoveHistory h = new MoveHistory(); AssignToMoveHistory(h); ObjScope.Add(h); return(true); }
public override Move PredictOpponentsNextMove(MoveHistory moveHistory) { this.LogOpponentsPreviousMove(moveHistory.GetOpponentsPreviousMove()); var random = _randomNumber.Next(0, _moves.Count - 1); CurrentPrediction = _moves[random]; return CurrentPrediction; }
public void ApplyMove(IGameMove move) { TicTacToeMove m = move as TicTacToeMove; SetPosition(m.Position, CurrentPlayer); MoveHistory.Add(m); mPlayer = -mPlayer; mGameOver = GameIsOver(); }
public void UndoLastMove() { TicTacToeMove m = MoveHistory.Last() as TicTacToeMove; SetPosition(m.Position, 0); mMoveHistory.RemoveAt(MoveHistory.Count - 1); mValue = 0; IsFinished = false; }
public void AddMove(MoveHistory move) { if (move == null) { throw new ArgumentNullException("move"); } _history.Add(turnNumber++, move); }
public void ApplyMove(IGameMove move) { TicTacToeMove m = move as TicTacToeMove; SetPosition(m.Position, CurrentPlayer); mWeight += mPlayer * mWeights[m.Position.Row, m.Position.Col]; MoveHistory.Add(m); mPlayer = -mPlayer; mGameOver = GameIsOver(); }
public void UndoLastMove() { TicTacToeMove m = MoveHistory.Last() as TicTacToeMove; SetPosition(m.Position, 0); Value = 0; mWeight += mPlayer * mWeights[m.Position.Row, m.Position.Col]; mPlayer = -mPlayer; MoveHistory.RemoveAt(MoveHistory.Count - 1); }
/// <summary> /// Plays the specified move and adds it to the move history. Corresponds to 'play'. /// </summary> /// <param name="move">The move.</param> public void Play(Move move) { MoveHistory.Push(Tuple.Create(move, this.Board)); Board board = this.Board.MakeMove(move, this.AllowSuicide); if (board == null) { throw new InvalidOperationException("Illegal move."); } this.Board = board; }
public void Optimize(int Iterations) { // Determine size of parameter space. var parameterSpace = 1d; var firstParticleInFirstSwarm = this[0].Particles[0]; for (var index = 0; index < firstParticleInFirstSwarm.Parameters.Count; index++) { var parameter = firstParticleInFirstSwarm.Parameters[index]; parameterSpace *= parameter.MaxValue - parameter.MinValue + 1; } _writeMessageLine($"Optimizing {firstParticleInFirstSwarm.Parameters.Count} parameters in a space of {parameterSpace:e2} discrete parameter combinations."); // Create game objects for each particle swarm. var boards = new Board[Count]; var searches = new Search[Count]; var evaluations = new Evaluation[Count]; for (var index = 0; index < Count; index++) { var board = new Board(_writeMessageLine); boards[index] = board; var cache = new Cache(1, board.ValidateMove); var killerMoves = new KillerMoves(Search.MaxHorizon); var moveHistory = new MoveHistory(); var evaluation = new Evaluation(board.IsRepeatPosition, () => false, _writeMessageLine); evaluations[index] = evaluation; searches[index] = new Search(cache, killerMoves, moveHistory, evaluation, () => false, _writeMessageLine); } var tasks = new Task[Count]; var bestEvaluationError = double.MaxValue; for (var iteration = 1; iteration <= Iterations; iteration++) { // Run iteration tasks on threadpool. _iterations = iteration; for (var index = 0; index < Count; index++) { var particleSwarm = this[index]; var board = boards[index]; var search = searches[index]; var evaluation = evaluations[index]; tasks[index] = Task.Run(() => particleSwarm.Iterate(board, search, evaluation)); } // Wait for all particle swarms to complete an iteration. Task.WaitAll(tasks); var bestParticle = GetBestParticle(); if (bestParticle.EvaluationError < bestEvaluationError) { bestEvaluationError = bestParticle.BestEvaluationError; } UpdateVelocity(); UpdateStatus(); } }
public void UndoLastMove() { TicTacToeMove m = MoveHistory.Last(); SetPosition(m.Position, 0); mMoveHistory.RemoveAt(MoveHistory.Count - 1); mWeight += mPlayer * mWeights[m.Position.Row, m.Position.Col]; mPlayer = -mPlayer; mValue = 0; IsFinished = false; }
public override Move PredictOpponentsNextMove(MoveHistory moveHistory) { if (!EnoughHistoryToPredictMove(moveHistory.GetMyHistory())) return null; LogOpponentsPreviousMove(moveHistory.GetOpponentsPreviousMove()); SetMatched(moveHistory.GetMyHistory()); CurrentPrediction = GetMatchedWithHightesWeight().Consequent; return CurrentPrediction; }
public ParticleSwarms(string pgnFilename, int particleSwarms, int particlesPerSwarm, int winScale, Delegates.DisplayStats displayStats, Core.Delegates.WriteMessageLine writeMessageLine) { _displayStats = displayStats; _writeMessageLine = writeMessageLine; // Load games. writeMessageLine("Loading games."); var stopwatch = new Stopwatch(); stopwatch.Start(); var board = new Board(writeMessageLine, UciStream.NodesInfoInterval); var pgnGames = new PgnGames(); pgnGames.Load(board, pgnFilename, writeMessageLine); stopwatch.Stop(); // Count positions. long positions = 0; for (var gameIndex = 0; gameIndex < pgnGames.Count; gameIndex++) { var pgnGame = pgnGames[gameIndex]; positions += pgnGame.Moves.Count; } var positionsPerSecond = (int)(positions / stopwatch.Elapsed.TotalSeconds); writeMessageLine($"Loaded {pgnGames.Count:n0} games with {positions:n0} positions in {stopwatch.Elapsed.TotalSeconds:0.000} seconds ({positionsPerSecond:n0} positions per second)."); stopwatch.Restart(); writeMessageLine("Creating data structures."); // Create parameters and particle swarms. var parameters = CreateParameters(); for (var particleSwarmsIndex = 0; particleSwarmsIndex < particleSwarms; particleSwarmsIndex++) { var particleSwarm = new ParticleSwarm(pgnGames, parameters, particlesPerSwarm, winScale); Add(particleSwarm); // Set parameter values of all particles in swarm to known best. for (var particleIndex = 0; particleIndex < particleSwarm.Particles.Count; particleIndex++) { SetDefaultParameters(particleSwarm.Particles[particleIndex].Parameters); } } var stats = new Stats(); var cache = new Cache(1, stats, board.ValidateMove); var killerMoves = new KillerMoves(Search.MaxHorizon); var moveHistory = new MoveHistory(); var eval = new Eval(stats, board.IsRepeatPosition, () => false, writeMessageLine); var search = new Search(stats, cache, killerMoves, moveHistory, eval, () => false, displayStats, writeMessageLine); var firstParticleInFirstSwarm = this[0].Particles[0]; firstParticleInFirstSwarm.CalculateEvaluationError(board, search, winScale); _originalEvaluationError = firstParticleInFirstSwarm.EvaluationError; stopwatch.Stop(); writeMessageLine($"Created data structures in {stopwatch.Elapsed.TotalSeconds:0.000} seconds."); }
public void Default_History_Is_Empty() { var history = new MoveHistory(); var myHistoryCount = history.GetMyHistory().ToList().Count; var opponentHistoryCount = history.GetMyOppenentsHistory().ToList().Count; var bothCount = history.GetMyAndOpponentsHistory().ToList().Count(); Assert.Equal(0, myHistoryCount); Assert.Equal(0, opponentHistoryCount); Assert.Equal(0, bothCount); }
public void AddMove_success() { //Arrange var move = new MoveHistory { move = _fixture.Create <Card>(), Player = _human.Object }; //Act //Assert Assert.DoesNotThrow(() => _state.AddMove(move)); }
public void draw(MoveHistory moveHistory) { this.whiteRemovedPieces.Controls.Clear(); this.blackRemovedPieces.Controls.Clear(); List <Piece> whiteRemovedPiece = new List <Piece>(); List <Piece> blackRemovedPiece = new List <Piece>(); foreach (Move move in moveHistory.getMoveHistory()) { if (move.isAttack()) { Piece attackedPiece = move.getAttackedPiece(); if (attackedPiece.getSide() == Sides.WHITE) { whiteRemovedPiece.Add(attackedPiece); } else { blackRemovedPiece.Add(attackedPiece); } } } foreach (Piece piece in whiteRemovedPiece) { Panel panel = new Panel(); panel.Size = new Size(60, 30); panel.Margin = new Padding(0); string alliance = piece.getSide() == Sides.WHITE ? "W" : "B"; string type = piece.getPieceType().getPieceName(); panel.BackgroundImage = Image.FromFile(Application.StartupPath + "\\images\\figures\\" + alliance + type + ".gif"); panel.BackgroundImageLayout = ImageLayout.Zoom; whiteRemovedPieces.Controls.Add(panel); } foreach (Piece piece in blackRemovedPiece) { Panel panel = new Panel(); panel.Size = new Size(60, 30); panel.Margin = new Padding(0); string alliance = piece.getSide() == Sides.WHITE ? "W" : "B"; string type = piece.getPieceType().getPieceName(); panel.BackgroundImage = Image.FromFile(Application.StartupPath + "\\images\\figures\\" + alliance + type + ".gif"); panel.BackgroundImageLayout = ImageLayout.Zoom; blackRemovedPieces.Controls.Add(panel); } this.Refresh(); }
public MoveResult PerformAMove() { To.SetOccupation(From.Piece); From.ClearOccupation(); MoveHistory.AddMove(From, To, To.Piece, _currentMove); _currentMove++; if (To.Piece.IsReadyToConvert(To.Y, To.Piece.Player.EnemiesKingsRow)) { return(MoveResult.NeedToConvert); } return(MoveResult.Success); }
public void Counts_Correct_When_History_Exists() { var history = new MoveHistory(); history.AddHistory(Moves.Rock, Moves.WaterBalloon); history.AddHistory(Moves.Scissors, Moves.Rock); var myHistoryCount = history.GetMyHistory().ToList().Count; var opponentHistoryCount = history.GetMyOppenentsHistory().ToList().Count; var bothCount = history.GetMyAndOpponentsHistory().ToList().Count(); Assert.Equal(2, myHistoryCount); Assert.Equal(2, opponentHistoryCount); Assert.Equal(2, bothCount); }
public ParticleSwarms(string PgnFilename, int ParticleSwarms, int ParticlesPerSwarm, int WinPercentScale, Delegates.WriteMessageLine WriteMessageLine) { _writeMessageLine = WriteMessageLine; // Load games. WriteMessageLine("Loading games."); var stopwatch = new Stopwatch(); stopwatch.Start(); var board = new Board(WriteMessageLine); var pgnGames = new PgnGames(); pgnGames.Load(board, PgnFilename); stopwatch.Stop(); // Count positions. long positions = 0; for (var gameIndex = 0; gameIndex < pgnGames.Count; gameIndex++) { var pgnGame = pgnGames[gameIndex]; positions += pgnGame.Moves.Count; } var positionsPerSecond = (int)(positions / stopwatch.Elapsed.TotalSeconds); WriteMessageLine($"Loaded {pgnGames.Count:n0} games with {positions:n0} positions in {stopwatch.Elapsed.TotalSeconds:0.000} seconds ({positionsPerSecond:n0} positions per second)."); stopwatch.Restart(); WriteMessageLine("Creating data structures."); // Create parameters and particle swarms. var parameters = CreateParameters(); for (var index = 0; index < ParticleSwarms; index++) { Add(new ParticleSwarm(pgnGames, parameters, ParticlesPerSwarm, WinPercentScale)); } // Set parameter values of first particle in first swarm to known best. var firstParticleInFirstSwarm = this[0].Particles[0]; SetDefaultParameters(firstParticleInFirstSwarm.Parameters); var cache = new Cache(1, board.ValidateMove); var killerMoves = new KillerMoves(Search.MaxHorizon); var moveHistory = new MoveHistory(); var evaluation = new Evaluation(board.IsRepeatPosition, () => false, WriteMessageLine); var search = new Search(cache, killerMoves, moveHistory, evaluation, () => false, WriteMessageLine); firstParticleInFirstSwarm.CalculateEvaluationError(board, search, WinPercentScale); _originalEvaluationError = firstParticleInFirstSwarm.EvaluationError; stopwatch.Stop(); WriteMessageLine($"Created data structures in {stopwatch.Elapsed.TotalSeconds:0.000} seconds."); }
private void RemoveLastMovesFromMoveHistory(int count) { for (int i = 0; i < count; ++i) { Player player = Plies % 2 == 0 ? Player.Black : Player.White; --Plies; if (player == Player.White) { MoveHistory.Rows.RemoveAt(MoveHistory.Rows.Count - 1); } else { MoveHistory.Last().BlackDetailedMove = null; } } }
public void Predictions_Are_Null_When_Not_Enough_History() { var os = new OpponentRulesStrategy(3, new SimpleFitness()); var history = new MoveHistory(); var res = os.PredictOpponentsNextMove(history); Assert.Null(res); history.AddHistory(Moves.Rock, Moves.Rock); history.AddHistory(Moves.Rock, Moves.Rock); res = os.PredictOpponentsNextMove(history); Assert.Null(res); }
public override Boolean doEvent(GameBoard gb) { MoveHistory h = gb.getHistory(); if (h == null) { return(true); } gb.board[h.row][h.col] = h.tile; gb.player.currentTile = gb.board[h.row][h.col]; gb.player.row = h.row; gb.player.col = h.col; return(true); }
public string GetTranscriptLine() { if (MoveHistory.Count > 0) { if (Settings.IndicateTranscriptMoveType) { var moves = GetDetailedMoveList(); return(moves.Select(x => x.Tile + (x.Addition ? "+" : "")).Aggregate((c, n) => c + " " + n)); } else { return(MoveHistory.Select(x => Constants.TileNames[x]).Aggregate((c, n) => c + " " + n)); } } return(""); }
public BoardGui(ChessPieceSide sidePlayFirst) : base() { this.boardLogic = new Board(sidePlayFirst); moveHistory = new MoveHistory(); this.CellSelectedFirst = this.CellSelectedSecond = null; this.ColumnCount = 8; this.RowCount = 8; this.Size = new Size(538, 538); this.Location = new Point(250, 40); this.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single; this.listCellGui = new List <CellGui>(); AddCellGuiAndCell(); }
public override Boolean doEvent() { if (gb.history.Count == 0) { return(false); } MoveHistory h = gb.history.Pop(); Tile t = h.tile; gb.board[t.row][t.col] = t; gb.player = h.player; return(false); }
public void TakeTurn(IGameState state) { //For simplicity the skill we assume is that this player will rank it's cards and play the card with the highest score /****************************** * ** card scoring rules ** * **************************** * * Suit with most cards is highest value suit * scores 4-1 * * add count below 7 vs count above 7 * * */ IDictionary <Card, int> scores = CalculateScore(); //Now we have our dictionary of best cards we need to find the highest scoring eligible card and play it var bestCards = scores.OrderByDescending(x => x.Value).Select(x => x.Key).ToList(); var move = new MoveHistory { Player = this }; foreach (var card in bestCards) { if (_board.IsEligible(card)) { //Play the card _board.SetGameBoardState(card); UpdateHand(card, true); move.move = card; state.AddMove(move); return; } } //if no cards are eligible, no cards are played. state.AddMove(move); }
public void Retreived_Values_Correct_Order() { var history = new MoveHistory(); history.AddHistory(Moves.Rock, Moves.WaterBalloon); history.AddHistory(Moves.Scissors, Moves.Rock); var myHistory = history.GetMyHistory().ToList(); var opponentHistory = history.GetMyOppenentsHistory().ToList(); var both = history.GetMyAndOpponentsHistory().ToList(); Assert.Equal(myHistory[0], Moves.Scissors); Assert.Equal(myHistory[1], Moves.Rock); Assert.Equal(opponentHistory[0], Moves.Rock); Assert.Equal(opponentHistory[1], Moves.WaterBalloon); Assert.True(both[0].Equals(Tuple.Create(Moves.Scissors, Moves.Rock))); Assert.True(both[1].Equals(Tuple.Create(Moves.Rock, Moves.WaterBalloon))); }
public void Can_Make_Random_Predictions() { var mock = new Mock<IRandomNumber>(); int calls = 0; mock.Setup(r => r.Next(0, 4)) .Returns(() => calls) .Callback(() => calls++); var randomMovePredictor = new RandomStrategy(mock.Object, new SimpleFitness()); var moveHistory = new MoveHistory(); moveHistory.AddHistory(Moves.Rock, Moves.Rock); Assert.Equal(randomMovePredictor.PredictOpponentsNextMove(moveHistory), Moves.Paper); Assert.Equal(randomMovePredictor.PredictOpponentsNextMove(moveHistory), Moves.Scissors); Assert.Equal(randomMovePredictor.PredictOpponentsNextMove(moveHistory), Moves.Rock); Assert.Equal(randomMovePredictor.PredictOpponentsNextMove(moveHistory), Moves.Dynamite); Assert.Equal(randomMovePredictor.PredictOpponentsNextMove(moveHistory), Moves.WaterBalloon); }
public Move PredictOpponentsNextMove(MoveHistory moveHistory) { var best = GetStrategyWithHighestFitness(); Move returnMove = null; foreach(var strategy in Strategies) { if (best == strategy) { returnMove = strategy.PredictOpponentsNextMove(moveHistory); } else { strategy.PredictOpponentsNextMove(moveHistory); } } return returnMove; }
public void TakeTurn(Card?card, IGameState state) { if (state == null) { throw new ArgumentNullException("state"); } var move = new MoveHistory { Player = this }; if (card != null) { _board.SetGameBoardState(card); UpdateHand(card, true); move.move = card; } state.AddMove(move); }
/// <summary> /// Undoes the last move, restoring the game to its state before the move was applied. /// </summary> public void UndoLastMove() { OthelloMove m = MoveHistory[MoveHistory.Count - 1] as OthelloMove; // Note: there is a bug in this code. if (!m.IsPass) { // Reset the board at the move's position. mBoard[m.Position.Row, m.Position.Col] = 0; Value += mCurrentPlayer; Weight += mCurrentPlayer * mWeights[m.Position.Row, m.Position.Col]; // Iterate through the move's recorded flipsets. foreach (var flipSet in m.FlipSets) { BoardPosition pos = m.Position; // For each flipset, walk along the flipset's direction resetting pieces. for (int i = 1; i <= flipSet.Count; i++) { pos = pos.Translate(flipSet.RowDelta, flipSet.ColDelta); mBoard[pos.Row, pos.Col] = (sbyte)mCurrentPlayer; Value += 2 * mCurrentPlayer; Weight += 2 * mCurrentPlayer * mWeights[pos.Row, pos.Col]; } } // Check to see if the second-to-last move was a pass; if so, set PassCount. if (MoveHistory.Count > 1 && (MoveHistory[MoveHistory.Count - 2] as OthelloMove).IsPass) { PassCount = 1; } } else { PassCount--; } // Reset the remaining game state. mCurrentPlayer = -mCurrentPlayer; MoveHistory.RemoveAt(MoveHistory.Count - 1); }
public void MakeMove(int move) { if (CurrentState.IsValidMove(move)) { bool growthHappened = CurrentState.MakeMove(move); MoveHistory.Add(move); if (growthHappened) { MoveHistory.Add(Constants.AllGrowMove); } OnMoveMade?.Invoke(growthHappened); if (CurrentState.State == GameState.GameOver) { OnGameOver?.Invoke(CurrentState.Winner, CurrentState.Winner == Player.Draw ? VictoryType.InfiniteEruption : VictoryType.AntipodePathCreation); } else { ComputerPlay(); } } }
public void TakeTurn(IGameState state) { var move = new MoveHistory { Player = this }; foreach (var card in _hand) { if (_board.IsEligible(card)) { //Play the card _board.SetGameBoardState(card); UpdateHand(card, true); move.move = card; state.AddMove(move); return; } } state.AddMove(move); }
public abstract Move PredictOpponentsNextMove(MoveHistory moveHistory);