/// <summary> /// Thinks about the current position given the dice roll and calculates the best move /// </summary> /// <param name="algorithm">Evaluation algorithm to use</param> /// <param name="roll">Roll</param> /// <param name="team">Team to think for</param> /// <param name="depth">Depth to think</param> public Move ThinkBest(Algorithm algorithm, DiceRoll roll, sbyte team, int ms) { MoveCollection collection = GetMoves(roll, team); if (collection.Count == 0) { return(null); } else if (collection.Count == 1) { return(collection[0]); } else if (ms < 0) { return(collection[random.Next(collection.Count)]); } PositionNode[] nodes = new PositionNode[collection.Count]; for (int i = 0; i < collection.Count; i++) { Position node = new Position(this.quadrants); PerformMove(node, collection[i]); nodes[i] = new PositionNode(node, collection[i], roll); } algorithm.Player = team; Vector4[] evaluations = new Vector4[nodes.Length]; for (int j = 0; j < nodes.Length; j++) { PositionNode currentRootMoveNode = nodes[j]; algorithm.Root = currentRootMoveNode; Vector4 moveEval = algorithm.Go(ms / collection.Count); evaluations[j] = moveEval; } int highIndex = 0; double high = evaluations[0].GetMagnitude(team); for (int i = 1; i < evaluations.Length; i++) { double curMag = evaluations[i].GetMagnitude(team); if (curMag > high) { high = curMag; highIndex = i; } } return(collection[highIndex]); }
/// <summary> /// Checks if the move is a possible move for a human (called by Engine) /// </summary> /// <param name="move">Move move</param> public bool IsPossibleMove(DiceRoll roll, Move move, out Move orderCorrect) { MoveCollection collection = GetMoves(roll, move.Team); for (int j = 0; j < collection.Count; j++) { if (collection[j].Equals(move)) { orderCorrect = collection[j]; return(true); } } orderCorrect = null; return(false); }
public MoveCollection ThinkAll(Algorithm algorithm, DiceRoll roll, sbyte team, int ms, out Vector4[] scores) { MoveCollection collection = GetMoves(roll, team); if (collection.Count == 0) { scores = null; return(null); } else if (collection.Count == 1) { scores = new Vector4[1] { algorithm.Eval(this.GetPosition(), roll.IsDoubles() ? team : Board.NO_PLAYER) }; return(collection); } if (ms < 0) { throw new ArgumentException(); } PositionNode[] nodes = new PositionNode[collection.Count]; for (int i = 0; i < collection.Count; i++) { Position node = new Position(this.quadrants); PerformMove(node, collection[i]); nodes[i] = new PositionNode(node, collection[i], roll); } algorithm.Player = team; Vector4[] evaluations = new Vector4[nodes.Length]; for (int j = 0; j < nodes.Length; j++) { PositionNode currentRootMoveNode = nodes[j]; algorithm.Root = currentRootMoveNode; Vector4 moveEval = algorithm.Go(ms / collection.Count); evaluations[j] = moveEval; } SortDecreasing(evaluations, collection, team); scores = evaluations; return(collection); }
/// <summary> /// Checks if the piece move is part of a valid move /// </summary> /// <param name="roll">Current roll</param> /// <param name="move">Move to check</param> /// <param name="team">Team</param> public bool IsPossibleTreeMove(DiceRoll roll, PieceMove move, sbyte team) { MoveCollection collection = GetMoves(roll, team); for (int j = 0; j < collection.Count; j++) { PieceMove[] moves = collection[j].PieceMoves; for (int i = 0; i < moves.Length; i++) { if (moves[i].Equals(move)) { return(true); } } } return(false); }
/// <summary> /// Starts thinking /// </summary> public void Think(DiceRoll roll, Board board) { int timeThink = Difficulty.TimeThink; Algorithm algorithm = GetAlgorithm(); if (timeThink < 0) { base.SetMove(board.ThinkBest(algorithm, roll, Team, timeThink)); return; } int blunderValue = Board.Next(100); if (blunderValue < Difficulty.BlunderPercent) { //Worst move Vector4[] scores; MoveCollection moves = board.ThinkAll(algorithm, roll, Team, ComputerPlayer.Difficulties[ComputerPlayer.Difficulties.Length - 1].TimeThink, out scores); LastEvaluation = GetVariations(moves, scores); if (moves != null) { base.SetMove(moves[moves.Count - 1]); return; } } else { //Best move Vector4[] scores; MoveCollection moves = board.ThinkAll(algorithm, roll, Team, Difficulty.TimeThink, out scores); LastEvaluation = GetVariations(moves, scores); if (moves != null) { base.SetMove(moves[0]); return; } } base.SetMove(null); }
public override void StartThink(Board board, DiceRoll roll) { base.StartThink(board, roll); //Continue logic if (thinkThread != null) { if (thinkThread.ThreadState == ThreadState.Running) { thinkThread.Abort(); thinkThread = new Thread(() => Think(roll, board)); } } thinkThread = new Thread(() => Think(roll, board)); thinkThread.Name = "MarbleBoard_AI"; thinkThread.IsBackground = true; DateTime start = DateTime.Now; thinkThread.Start(); }
private double Iterate(Position pos, sbyte us, sbyte evaluating_team, int time) { Stopwatch sw = Stopwatch.StartNew(); double wins = 0; double losses = 0; Board board = new Board(); sbyte team = NextTeam(us); int doublesCount = 0; sbyte lastTeam = 0; int turns = 0; do { //Reset board board.SetPosition(pos); //Pick random nodes and play out game while (!board.IsGameOver()) { lastTeam = team; DiceRoll roll = board.GetRandomRoll(); MoveCollection moves = board.GetMoves(roll, team); if (moves.Count != 0) { Move m = moves[Board.Next(moves.Count)]; if (m != null) { board.PerformMove(m); } } if (roll.IsDoubles()) { doublesCount++; if (doublesCount >= 3) { board.RemoveFrontMarble(team); doublesCount = 0; team = board.NextPlayer(team); turns++; } } else { doublesCount = 0; team = board.NextPlayer(team); turns++; } } if (lastTeam == evaluating_team) { wins++; } else { losses++; } }while (sw.ElapsedMilliseconds < time); return(wins / losses); }
/// <summary> /// Gets all the possible moves for a team given a dice roll /// </summary> /// <param name="roll">Dice roll</param> /// <param name="team">Marble team</param> public MoveCollection GetMoves(Position position, DiceRoll roll, sbyte team) { if (team < 0 || team > TEAM_COUNT) { return(null); } sbyte winner = -1; if (position.IsWon(out winner)) { return(null); } MoveCollection moves = new MoveCollection(this, position); Square[] marbleSquares = GetMarbles(position, team); bool hasMarbleOnSpawn = position.Get(new Square(team, 0)) == team; int activePieces = GetActivePiecesCount(marbleSquares); //Loop until a marble in our base is found for (int i = 0; i < marbleSquares.Length; i++) { //If marble is at start square if (marbleSquares[i] == null) { //If no team marble is on spawn square if (!hasMarbleOnSpawn) { int[] takeOut = roll.CanTakeOutWith(); if (takeOut.Length > 0) { for (int j = 0; j < takeOut.Length; j++) { Square target = new Square(team, takeOut[j]); if (position.Get(target) != team) { moves.Add(new Move(new PieceMove(null, target), team)); } } } } //Add a special case for taking out a piece and using the other die value on another marble if (activePieces > 0) { int[] combinations = roll.GetDoublesTakeOutCombinations(); PieceMove takeOutCombo = new PieceMove(null, new Square(team, 0)); for (int p = 0; p < activePieces; p++) { for (int pc = 0; pc < combinations.Length; pc++) { PieceMove correspondant = new PieceMove(marbleSquares[p], marbleSquares[p].Add(combinations[pc], team)); if (hasMarbleOnSpawn && !correspondant.From.Equals(takeOutCombo.To)) { continue; } moves.Add(new Move(new PieceMove[] { correspondant, takeOutCombo }, team)); } } } break; } } List <int[]> pieceCombinations = GetPieceCombinations(activePieces); for (int c = 0; c < pieceCombinations.Count; c++) { int pieces = pieceCombinations[c].Length; int[][] pieceValues = roll.GetValues(pieces); for (int k = 0; k < pieceValues.Length; k++) { PieceMove[] pieceMoves = new PieceMove[pieces]; for (int j = 0; j < pieces; j++) { Square marblePiece = marbleSquares[pieceCombinations[c][j]]; pieceMoves[j] = new PieceMove(marblePiece, marblePiece.Add(pieceValues[k][j], team)); if (j > 0) { if (pieceMoves[j].From.Equals(pieceMoves[j - 1].To)) { //Swap the move order PieceMove current = pieceMoves[j]; pieceMoves[j] = pieceMoves[j - 1]; pieceMoves[j - 1] = current; } } } Move move = new Move(pieceMoves, team); moves.Add(move); } } return(moves); }
/// <summary> /// Gets all the possible moves for a team given a dice roll /// </summary> /// <param name="roll">Dice roll</param> /// <param name="team">Marble team</param> public MoveCollection GetMoves(DiceRoll roll, sbyte team) { return(GetMoves(GetPosition(), roll, team)); }
/// <summary> /// Updates the view of the board /// </summary> /// <param name="gameTime">Current GameTime</param> public void Update(GameTime gameTime) { //Update logic KeyboardState kState = Keyboard.GetState(); MouseState mState = MouseHandle.GetState(); //Updates squares UpdateSquares(); if (Playing) { //Update current animation if (currentAnimation != null) { currentAnimation.Update(gameTime); } if (kState.IsKeyDown(Keys.Q) && rollOver) { for (int i = 0; i < currentMove.Count; i++) { if (currentMove[i].IsTakingOutMarble) { PutBackMarble(currentPlayer.Team); } } RemoveMove(); } if (!currentPlayer.IsThinking && CanRoll()) { if (!currentPlayer.IsHuman) { rollOver = false; RollDice(gameTime); } else if (!isDiceRolling && kState.IsKeyDown(Keys.R)) //Check for user starting a roll { rollOver = false; RollDice(gameTime); } } //Check if dice are done rolling if (die1 != -1 && die2 != -1 && !rollOver) { currentRoll = new DiceRoll(die1, die2); OnRollFinished(); } //Check if dice have rolled for too long if (rollStartTime.HasValue) { TimeSpan timeElapsed = gameTime.TotalGameTime.Subtract(rollStartTime.Value); double timeElapsedMs = timeElapsed.TotalMilliseconds; if (timeElapsedMs >= 10000) { StopDice(); UpdateDie(die1Body, ref die1, true); UpdateDie(die2Body, ref die2, true); if (die1 != -1 && die2 != -1) { currentRoll = new DiceRoll(die1, die2); OnRollFinished(); } } } //If the player is a human and hasnt rolled yet, disallow any movement of marbles if (!CanRoll()) { //Update drag UpdateDrag(mState); ////Handle user submitting move if (rollOver && kState.IsKeyDown(Keys.Enter) && currentPlayer.IsHuman) { if (!HasMove) { //Pressed enter without making a move MoveCollection collection = board.GetMoves(currentRoll, currentPlayer.Team); if (collection.Count == 0) { //End the player step ProcessRoll(); SetReadyForRoll(); // } } else { if (currentRoll == null) { RemoveMove(); } else { Move move = GetMove(currentPlayer.Team); if (board.IsPossibleMove(currentRoll, move, out move)) { currentPlayer.SetMove(move); } else { for (int i = 0; i < currentMove.Count; i++) { if (currentMove[i].IsTakingOutMarble) { PutBackMarble(currentPlayer.Team); } } RemoveMove(); } } } } } if (currentPlayer.HasMove) { OnMoveFound(currentPlayer.GetMove()); } //Update dice UpdateDie(die1Body, ref die1, IsNearlyZero(die1Body.LinearVelocity)); UpdateDie(die2Body, ref die2, IsNearlyZero(die2Body.LinearVelocity)); if (!Resizing) { world.Step(1 / 30f, false); //world.Step((float)gameTime.TotalGameTime.TotalSeconds, false, (float)gameTime.ElapsedGameTime.TotalSeconds, 1); } } }
/// <summary> /// Creates a new position node /// </summary> /// <param name="value">Position value</param> /// <param name="roll">Dice roll</param> public PositionNode(Position value, Move move, DiceRoll roll) : this() { Value = value; Move = move; Roll = roll; }
public static sbyte SimulateGame() { Board board = new Board(); Algorithm mpMix = new MPMix(board, 1, 0.5); Algorithm maxN = new MaxN(board); Algorithm paranoid = new Paranoid(board); Algorithm offensive = new Offensive(board); Algorithm mcts = new MCTS(board); int doublesCount = 0; sbyte lastTeam = 0; sbyte team = 0; int turns = 0; while (!board.IsGameOver()) { Console.Title = turns.ToString(); lastTeam = team; DiceRoll roll = board.GetRandomRoll(); Move m = null; switch (team) { case 0: m = board.ThinkBest(mcts, roll, team, 0); break; case 1: m = board.ThinkBest(maxN, roll, team, 0); break; case 2: m = board.ThinkBest(mcts, roll, team, 0); break; case 3: m = board.ThinkBest(mcts, roll, team, 0); break; } if (m != null) { board.PerformMove(m); } if (roll.IsDoubles()) { doublesCount++; if (doublesCount >= 3) { board.RemoveFrontMarble(team); doublesCount = 0; team = board.NextPlayer(team); turns++; } } else { doublesCount = 0; team = board.NextPlayer(team); turns++; } } Console.WriteLine("{0} won ({1} turns)", GetName(lastTeam), turns); return(lastTeam); }
/// <summary> /// Evaluates a teams overall score in units of scored marbles (5 = won) /// </summary> /// <param name="position">Position to evaluate for a team</param> /// <param name="team"></param> /// <returns></returns> public double EvaluateTeam(Position position, sbyte team, bool rolledDoubles) { //Score in units of scores (5 = won) double score = 0.0; const double marbleValue = 0.1; //Sorted list of marbles Square[] marbles = GetMarbles(position, team); int activeMarbles = ActiveMarbles(marbles); int activePathMarbles = ActivePathMarbles(marbles); int[] activeEnemyMarbleCount = null; Square[] enemies = GetEnemyMarbles(position, team, out activeEnemyMarbleCount); //Evaluation scorelist //O-Check for impossible position with roll for 1 to win //O-Evaluate distance to goal) //O-Evaluate danger (probability of being taken) (Add special case for being on enemy spawn squares?) //O-Evaluate scored marbles (Add bonus for being aligned back in a row) //-Check for impossible position with roll for 1 to win if (activeMarbles == Board.TEAM_SIZE) { if (MustRollDoubles3x(position, team)) { return(DiceRoll.P_ROLLING_DOUBLES_3X); } } //-Evaluate distance to goal //-Pentalize marbles on own goal (for taking out) Square endGoal = GetEndGoal(position, team); double endIndex = endGoal.GetBoardIndex(team); double distanceScore = 0.0; for (int i = 0; i < marbles.Length; i++) { if (marbles[i] != null) { if (marbles[i].IsInPath()) { if (marbles[i].GetBoardIndex(team) == 1) { score -= marbleValue; } double dis = (endIndex - marbles[i].DistanceTo(endGoal, team)); distanceScore += dis; } } } //Add score for activity score += activeMarbles * marbleValue; //Add the distance traveled if (activeMarbles > 0) { score += (distanceScore - 1) / (double)(endIndex * activeMarbles); } //-Evaluate danger (probability of being taken) (Add special case for being on enemy spawn squares?) double dangerScore = 0.0f; if (!rolledDoubles) { //Add special case for being on enemy spawn squares for (int m = 0; m < marbles.Length; m++) { if (marbles[m] != null) { double weight = 2 * ((endIndex - marbles[m].DistanceTo(endGoal, team)) / endIndex); for (sbyte eT = 0; eT < Board.TEAM_COUNT; eT++) { if (eT != team) { int marbleCount = activeEnemyMarbleCount[eT]; if (marbleCount > 0) { Square enemySpawn = new Square(eT, 0); if (marbles[m].Equals(enemySpawn)) { dangerScore += weight * DiceRoll.P_GETTING_OUT; } } } } } } for (int x = 0; x < enemies.Length; x++) { if (enemies[x] != null) { if (enemies[x].IsInPath()) { for (int t = 0; t < marbles.Length; t++) { if (marbles[t] != null) { if (marbles[t].IsInPath()) { double weight = (endIndex - marbles[t].DistanceTo(endGoal, team)) / endIndex; int distance = marbles[t].DistanceTo(enemies[x], team); if (distance > 0) { if (distance <= 6) { int marbleTeam = position.Get(enemies[x]); int activeEnemyMarbles = activeEnemyMarbleCount[marbleTeam]; //Active in path if (activeEnemyMarbles >= 2) { dangerScore += weight * DiceRoll.P_ANY_NUM; } else if (activeEnemyMarbles >= 1) { dangerScore += weight * (DiceRoll.P_ANY_NUM * DiceRoll.P_GETTING_OUT); } else { double probability = DiceRoll.GetProbabilityQuick(distance); dangerScore += weight * probability; } } else if (distance < 36) //If greater than, the probability is 0, so no danger { double probability = DiceRoll.GetProbabilityQuick(distance); dangerScore += weight * probability; } } } } } } } } //Subtract danger score score -= dangerScore; } double eDistanceScore = 0.0; double eDividendTotal = 0.0; int eCount = 0; //O-Evaluate other marbles for (sbyte sEM = 0; sEM < enemies.Length; sEM++) { if (enemies[sEM] != null) { if (enemies[sEM].IsInPath()) { sbyte eTeam = (sbyte)position.Get(enemies[sEM]); Square eEndGoal = GetEndGoal(position, eTeam); double eDividend = eEndGoal.GetBoardIndex(eTeam); eDistanceScore += (eDividend - enemies[sEM].DistanceTo(eEndGoal, eTeam)); eDividendTotal += eDividend; eCount++; } } } if (eCount > 0) { //We don't want a full negative impact from enemy moves (between 1/2-3/4 scale factor) score -= 0.55 * (eDistanceScore / eDividendTotal); } //-Evaluate scored marbles (Add bonus for being aligned back in a row) bool inARow = true; int marblesScored = 0; double marblesScoredScore = 0; //Marbles as far back as possible = 1 score Square[] homeSquares = GetHomeSquares(team); for (int i = homeSquares.Length - 1; i >= 0; i--) { int marble = position.Get(homeSquares[i]); if (marble == team) { if (inARow) { marblesScoredScore++; } else { marblesScoredScore += 0.5 + (0.5 / (double)(homeSquares.Length - i)); } marblesScored++; } else { //Gap, stop giving points inARow = false; } } score += marblesScoredScore; return(score); }