/// <summary> /// Returns the table with updated stability values after given move. /// </summary> /// <param name="table">Table after the move with original stability state.</param> /// <returns>The table after given move with updated stability values.</returns> public static ReversiTable GetTableWithUpdatedStability(this ReversiTable table) { // Start with all currently unstable cells. Queue <Position> unstableCells = new Queue <Position>(table.GetUnstableCells().ToList()); // Process all unstable cells. while (unstableCells.Count > 0) { Position candidate = unstableCells.Dequeue(); if (!table.GetStable(candidate.Item1, candidate.Item2) && table.IsStable(candidate)) { // Mark the cell as stable. table.SetStable(true, candidate.Item1, candidate.Item2); // All unstable neighbors need to be checked again. foreach (Position delta in ReversiConstants.Directions) { Position neighbor = new Position(candidate.Item1 + delta.Item1, candidate.Item2 + delta.Item2); if (neighbor.Item1 >= 0 && neighbor.Item1 < ReversiTable.Size && neighbor.Item2 >= 0 && neighbor.Item2 < ReversiTable.Size && table.GetValue(neighbor.Item1, neighbor.Item2) != Value.None && !table.GetStable(neighbor.Item1, neighbor.Item2)) { unstableCells.Enqueue(neighbor); } } } } return(table); }
/// <summary> /// Returns the table after given move. /// </summary> /// <param name="move">The move.</param> /// <returns>The table after given move.</returns> private ReversiTable GetTableForMove(Tuple <Position, IEnumerable <Position> > move) { Position cell = move.Item1; ReversiTable tableAfterMove = stateTable; tableAfterMove.SetValue(Player, cell.Item1, cell.Item2); foreach (Position direction in move.Item2) { int dx = cell.Item1 + direction.Item1; int dy = cell.Item2 + direction.Item2; while ( dx >= 0 && dx < ReversiTable.Size && dy >= 0 && dy < ReversiTable.Size && stateTable.GetValue(dx, dy) == Opponent) { tableAfterMove.SetValue(Player, dx, dy); dx += direction.Item1; dy += direction.Item2; } } return(tableAfterMove); }
/// <summary> /// Gets the list of currently unstable occupied cells that are potentially stable. /// </summary> /// <param name="table">The table.</param> /// <returns>The list of unstable cells.</returns> public static IEnumerable <Position> GetUnstableCells(this ReversiTable table) { for (int i = 0; i < ReversiTable.Size; ++i) { for (int j = 0; j < ReversiTable.Size; ++j) { if (table.GetValue(i, j) != Value.None && !table.GetStable(i, j)) { yield return(new Position(i, j)); } } } }
/// <summary> /// Gets the stability score. /// </summary> /// <param name="table">The table.</param> /// <returns>The difference between maximizing and minimizing players' stable cells.</returns> public static int GetStabilityScore(this ReversiTable table) { int score = 0; for (int i = 0; i < ReversiTable.Size; ++i) { for (int j = 0; j < ReversiTable.Size; ++j) { if (table.GetStable(i, j)) { score += table.GetValue(i, j) == Value.Maximizing ? 1 : -1; } } } return(score); }
/// <summary> /// Initializes a new instance of the <see cref="ReversiNode" /> class. /// </summary> /// <param name="table">The game state.</param> /// <param name="currentPlayer">Current player.</param> /// <param name="playerCanPass">Indicates whether the current player can pass a move.</param> private ReversiNode( ReversiTable table, Value currentPlayer, bool playerCanPass = true) { canPass = playerCanPass; stateTable = table; heuristics = new Lazy <int>( () => GetHeuristics(), LazyThreadSafetyMode.ExecutionAndPublication); children = new Lazy <IReadOnlyList <ReversiNode> >( () => GetChildren(), LazyThreadSafetyMode.ExecutionAndPublication); Debug.Assert(currentPlayer != Value.None, "Verifying that the player value is valid."); Player = currentPlayer; Opponent = currentPlayer == Value.Maximizing ? Value.Minimizing : Value.Maximizing; }
/// <summary> /// Initializes a new instance of the <see cref="ReversiNode" /> class. /// </summary> /// <param name="table">The game state.</param> /// <param name="currentPlayer">Current player.</param> /// <param name="playerCanPass">Indicates whether the current player can pass a move.</param> private ReversiNode( ReversiTable table, Value currentPlayer, bool playerCanPass = true) { canPass = playerCanPass; stateTable = table; heuristics = new Lazy<int>( () => GetHeuristics(), LazyThreadSafetyMode.ExecutionAndPublication); children = new Lazy<IReadOnlyList<ReversiNode>>( () => GetChildren(), LazyThreadSafetyMode.ExecutionAndPublication); Debug.Assert(currentPlayer != Value.None, "Verifying that the player value is valid."); Player = currentPlayer; Opponent = currentPlayer == Value.Maximizing ? Value.Minimizing : Value.Maximizing; }
/// <summary> /// Computes all children nodes of current node. /// </summary> /// <returns>Children nodes.</returns> private IReadOnlyList <ReversiNode> GetChildren() { var children = GetValidMoves(Player).Select(move => { ReversiTable table = GetTableForMove(move); ReversiTable stabilityTable = table.GetTableWithUpdatedStability(); return(new ReversiNode(stabilityTable, Opponent)); }).ToList(); // Check whether it is a terminal node or whether the current player passes. if (children.Count == 0 && canPass) { ReversiNode nextNode = new ReversiNode(stateTable, Opponent, false); if (nextNode.Children.Count > 0) { children.Add(nextNode); } } return(children.AsReadOnly()); }
/// <summary> /// Initializes a new instance of the <see cref="ReversiNode" /> class with the starting position. /// </summary> public ReversiNode() : this( ReversiTable.InitialState(), Value.Maximizing) { }
/// <summary> /// Determines whether the specified cell is stable. /// </summary> /// <param name="table">The table.</param> /// <param name="cell">The cell.</param> /// <returns>True if the cell is stable, false otherwise.</returns> public static bool IsStable(this ReversiTable table, Position cell) { if (table.GetValue(cell.Item1, cell.Item2) == Value.None) { return(false); } foreach (Position delta in ReversiConstants.StabilityDirections) { Value player = table.GetValue(cell.Item1, cell.Item2); Position positive = new Position(cell.Item1 + delta.Item1, cell.Item2 + delta.Item2); Position negative = new Position(cell.Item1 - delta.Item1, cell.Item2 - delta.Item2); // If one of the cell is on the edge, the direction is stable. if (positive.Item1 < 0 || positive.Item1 >= ReversiTable.Size || positive.Item2 < 0 || positive.Item2 >= ReversiTable.Size || negative.Item1 < 0 || negative.Item1 >= ReversiTable.Size || negative.Item2 < 0 || negative.Item2 >= ReversiTable.Size) { continue; } // If at least one of the neighbors is a stable cell belonging to the same player, // the direction is stable. if ((table.GetStable(positive.Item1, positive.Item2) && table.GetValue(positive.Item1, positive.Item2) == player) || (table.GetStable(negative.Item1, negative.Item2) && table.GetValue(negative.Item1, negative.Item2) == player)) { continue; } // If all cells in the line of direction are filled, the direction is stable. do { if (table.GetValue(positive.Item1, positive.Item2) == Value.None) { return(false); } positive = new Position(positive.Item1 + delta.Item1, positive.Item2 + delta.Item2); }while (positive.Item1 >= 0 && positive.Item1 < ReversiTable.Size && positive.Item2 >= 0 && positive.Item2 < ReversiTable.Size); do { if (table.GetValue(negative.Item1, negative.Item2) == Value.None) { return(false); } negative = new Position(negative.Item1 - delta.Item1, negative.Item2 - delta.Item2); }while (negative.Item1 >= 0 && negative.Item1 < ReversiTable.Size && negative.Item2 >= 0 && negative.Item2 < ReversiTable.Size); // The line is filled. continue; } // No unstable directions found. return(true); }