/// <summary> /// In Nim a valid move is defined as removing from 1 to all tokens from 1 row, /// this methods verifies that the difference between the first parameter /// and the second constitutes a valid move. /// </summary> /// <param name="currentState">The current state of the game board</param> /// <param name="proposedState">The state which an AI or Player's move will result in</param> /// <returns>If the move the AI or Player is making is valid</returns> public static bool IsMoveValid(BoardState currentState, BoardState proposedState) { if (currentState.RowCounts[0] > proposedState.RowCounts[0] && proposedState.RowCounts[0] >= 0) { if (currentState.RowCounts[1] == proposedState.RowCounts[1] && currentState.RowCounts[2] == proposedState.RowCounts[2]) return true; } if (currentState.RowCounts[1] > proposedState.RowCounts[1] && proposedState.RowCounts[1] >= 0) { if (currentState.RowCounts[0] == proposedState.RowCounts[0] && currentState.RowCounts[2] == proposedState.RowCounts[2]) return true; } if (currentState.RowCounts[2] > proposedState.RowCounts[2] && proposedState.RowCounts[2] >= 0) { if (currentState.RowCounts[0] == proposedState.RowCounts[0] && currentState.RowCounts[1] == proposedState.RowCounts[1]) return true; } return false; }
public void RunPlayerVersusAIGame() { GameState = new BoardState(); MoveList.Clear(); Random randomGen = new Random(); //Randomly choose the AI to take first turn. if (randomGen.Next(2) % 2 == 0) { GameState = NimAI_1.TakeTurn(GameState); MoveList.Add(GameState); Console.WriteLine(); GameState.PrintBoard(); Console.WriteLine("After AI Turn. Any Key To Continue"); Console.ReadLine(); } do { Console.Clear(); GameState.PrintBoard(); GameState = UserConsoleInput.GetPlayerTurnInput(GameState); MoveList.Add(GameState); Console.WriteLine(); Console.WriteLine("After Player Turn."); GameState.PrintBoard(); if (GameState.TotalLeft <= 0) { GameOver("AI_1"); break; } GameState = NimAI_1.TakeTurn(GameState); MoveList.Add(GameState); Console.WriteLine(); Console.WriteLine("After AI Turn"); GameState.PrintBoard(); if (GameState.TotalLeft <= 0) { GameOver("Player"); break; } Console.WriteLine("Press Any Key To Continue. "); Console.ReadLine(); } while (Playing); NimLogic.WeightBoardStates(ref MoveList); NimAI_1.IntegrateIntoCatalog(MoveList); Console.ReadLine(); }
public BoardState(BoardState b) { this.RowCounts = new int[3]; for (int r = 0; r < RowCounts.Length; r++) this.RowCounts[r] = b.RowCounts[r]; this.StateValue = b.StateValue; this.Frequency = b.Frequency; }
/// <summary> /// Used to determine all possible moves based upon the current BoardState /// </summary> /// <param name="currentState">Current Game's BoardState</param> /// <param name="weightedStateList">List to look for possible moves from</param> /// <returns>Each valid move from the provided list</returns> public static IEnumerable<BoardState> GetAllValidMoves(BoardState currentState, List<BoardState> weightedStateList) { foreach (var bs in weightedStateList) { if (IsMoveValid(currentState, bs)) yield return bs; } }
public static BoardState EmptyBoardState() { BoardState emptyBoard = new BoardState(); emptyBoard.RowCounts[0] = 0; emptyBoard.RowCounts[1] = 0; emptyBoard.RowCounts[2] = 0; emptyBoard.StateValue = -1000; return emptyBoard; }
/// <summary> /// Runs the logic for an AI to make a move. Iterates through all valid moves and returns the highest weighted possible move. /// </summary> /// <param name="currentState">Current Game's BoardState</param> /// <returns>The AI's chosen move, guaranteed to be a valid move.</returns> public BoardState TakeTurn(BoardState currentState) { BoardState moveToMake = BoardState.EmptyBoardState(); Random random = new Random(); foreach(var b in NimLogic.GetAllValidMoves(currentState, BoardStateCatalog)) { if (b.StateValue > moveToMake.StateValue) moveToMake = new BoardState(b); else if (b.StateValue == moveToMake.StateValue) if(random.Next(2) == 0) moveToMake = new BoardState(b); } return moveToMake; }
/// <summary> /// Used to provide a list of all possible BoardStates. /// </summary> /// <returns></returns> public static IEnumerable<BoardState> EveryPossibleBoardState() { for (int rowA = 0; rowA <= 3; rowA++) { for (int rowB = 0; rowB <= 5; rowB++) { for (int rowC = 0; rowC <= 7; rowC++) { BoardState b = new BoardState(); b.Frequency = 0; b.StateValue = 0; b.RowCounts[0] = rowA; b.RowCounts[1] = rowB; b.RowCounts[2] = rowC; yield return b; } } } }
public void RunAIVersusAIGame() { GameState = new BoardState(); MoveList.Clear(); Random randomGen = new Random(); if (randomGen.Next(2) % 2 == 0) { GameState = NimAI_2.TakeTurn(GameState); MoveList.Add(GameState); } do { GameState = NimAI_1.TakeTurn(GameState); MoveList.Add(GameState); if (GameState.TotalLeft <= 0) { GameOver("AI_2"); break; } GameState = NimAI_2.TakeTurn(GameState); MoveList.Add(GameState); if (GameState.TotalLeft <= 0) { GameOver("AI_1"); break; } } while (Playing); NimLogic.WeightBoardStates(ref MoveList); NimAI_1.IntegrateIntoCatalog(MoveList); NimAI_2.IntegrateIntoCatalog(MoveList); }
/// <summary> /// Runs through the logic required to create a new BoardState based upon /// the currentState and user input. First queries the user for which row using /// ForceConsoleIntegerInput and checks that the row has more than 0 tokens. /// Then asks for how many tokens to remove using ForceConsoleIntegerInput. /// </summary> /// <param name="currentState">Current game's BoardState</param> /// <returns>A BoardState created from the currentState and UserInput</returns> public static BoardState GetPlayerTurnInput(BoardState currentState) { BoardState newState = new BoardState(currentState); do { int row = 0; bool validChoice = false; do { row = ForceConsoleIntegerInput("Choose a Row(1-3): ", "Invalid Input.", 1, 3); validChoice = newState.RowCounts[row - 1] > 0; } while (!validChoice); newState.RowCounts[row - 1] -= ForceConsoleIntegerInput("Choose an amount(1-" + currentState.RowCounts[row - 1] + "): ", "Invalid Input.", 1, currentState.RowCounts[row - 1]); } while (!NimLogic.IsMoveValid(currentState, newState)); return newState; }
public void ApplyNewData(BoardState state) { float totalValue = (float)Frequency * StateValue; totalValue += state.StateValue; Frequency++; StateValue = totalValue / Frequency; }
public bool Equals(BoardState board) { if (board.RowCounts.Length != this.RowCounts.Length) return false; for(int r = 0; r < RowCounts.Length; r++) if(board.RowCounts[r] != this.RowCounts[r]) return false; return true; }