protected override int EstimationFunction(MinimaxTreeNode node) { // winning condition for me if (WhoHasMoreBoxes(node, out int blue, out int red) == whoAmI && node.NonExistingLines.Count == 0) { return(int.MaxValue); } List <Box> newBoxes = AICommon.TryClosingBoxes(node.ExistingLines, (node.Player == MinimaxPlayerType.MAX ? Player.BLUE : Player.RED), node.DeltaMove, out int[] surroundingEdges); // select state in which user can close boxes if (newBoxes.Count > 0) { return(int.MaxValue - 1); } // this state could lead opponent to close the box, hence negative estimation if (surroundingEdges[0] == 2 && surroundingEdges[1] == 2) { return(-2); } else if (surroundingEdges[0] == 2 || surroundingEdges[1] == 2) { return(-1); } // state is neutral for result for the provided tree depth return(0); }
protected override int EstimationFunction(MinimaxTreeNode node) { int blue, red; // winning condition for me if (WhoHasMoreBoxes(node, out blue, out red) == whoAmI && node.NonExistingLines.Count == 0) { return(int.MaxValue); } // winning condition for opponent else if (WhoHasMoreBoxes(node, out blue, out red) != whoAmI && node.NonExistingLines.Count == 0) { return(int.MinValue); } List <Box> newBoxes = AICommon.TryClosingBoxes(node.ExistingLines, (node.Player == MinimaxPlayerType.MAX ? Player.BLUE : Player.RED), node.DeltaMove, out int[] surroundingEdges); // box closing if (newBoxes.Count == 1) { return(int.MaxValue - 2); // will close one box } else if (newBoxes.Count == 2) { return(int.MaxValue - 1); // will close two boxes } // keep state where I will have more boxes if ((blue > red && whoAmI == Player.BLUE) || (red > blue && whoAmI == Player.RED)) { return(int.MaxValue / 2 - Math.Abs(blue - red)); } // this state could lead opponent to close the box, hence negative estimation if (surroundingEdges[0] == 2 && surroundingEdges[1] == 2) { return(-2); } else if (surroundingEdges[0] == 2 || surroundingEdges[1] == 2) { return(-1); } if ((blue < red && whoAmI == Player.BLUE) || (red < blue && whoAmI == Player.RED)) { return(int.MinValue / 2 + Math.Abs(blue - red)); } // state is neutral for result for the provided tree depth return(0); }
private void FinishTurn(LineBetweenCircles line) { if (line == null) { throw new Exception("AI returned null for the next move."); } // will return false if clicked on line that had already been drawn on the canvas if (!TransferFromNonExistingToExisting(line)) { return; } /////////////////////////////////// string log = line.ToString(); int startPosition = turnRichTextBox.Text.Length; string textToAppend = log + Environment.NewLine; turnRichTextBox.AppendText(textToAppend); turnRichTextBox.SelectionStart = startPosition; turnRichTextBox.SelectionLength = log.Length; turnRichTextBox.SelectionColor = (currentGame.Turn == Player.BLUE ? Color.Blue : Color.Red); turnRichTextBox.ScrollToCaret(); /////////////////////////////////// List <Box> newBoxes = AICommon.TryClosingBoxes(existingCanvasLines, currentGame.Turn, line, out int[] notUsed); boxes.AddRange(newBoxes); currentGame.Score[(int)currentGame.Turn] += newBoxes.Count; if (newBoxes.Count > 0) { canvas.Refresh(); UpdateGUI(); } if (boxes.Count == (currentGame.TableSizeX - 1) * (currentGame.TableSizeY - 1)) { currentGame.GameOver = true; } if (currentGame.GameOver) { UpdateGUI(); GUI_GameSettingChanged(null, null); MessageBox.Show("Igra je završena"); } else if (currentGame.Opponent2 == null) // pc vs pc will be handled in other method -> PcNextStep { if (currentGame.Turn == Player.BLUE) { if (newBoxes.Count == 0) { SwitchTurn(); if (currentGame.Opponent != null) { FinishTurn(currentGame.Opponent.MakeTurn()); } } UpdateGUI(); } else { if (newBoxes.Count == 0) { SwitchTurn(); } else if (newBoxes.Count > 0 && currentGame.Opponent != null) { UpdateGUI(); Thread.Sleep(1000); FinishTurn(currentGame.Opponent.MakeTurn()); } UpdateGUI(); } } else if (currentGame.Opponent != null && currentGame.Opponent2 != null) { if (newBoxes.Count == 0) { SwitchTurn(); } UpdateGUI(); canvas.Refresh(); } }
private int ConstructTree(MinimaxTreeNode parentNode, int depth, MinimaxPlayerType minimaxPlayer, int alpha, int beta) { // return estimation function if max tree depth reached or game is in finished state if (depth == maxTreeDepth || parentNode.NonExistingLines.Count == 0) { return(EstimationFunction(parentNode)); } if (minimaxPlayer == MinimaxPlayerType.MAX) { int bestValue = int.MinValue; for (int i = 0; i < parentNode.NonExistingLines.Count; i++) { #region New Node Creation MinimaxTreeNode node = new MinimaxTreeNode(); // calculate estimation function only for leaf nodes node.Player = (parentNode.Player == MinimaxPlayerType.MAX ? MinimaxPlayerType.MIN : MinimaxPlayerType.MAX); node.ExistingLines.AddRange(parentNode.ExistingLines); node.ExistingLines.Add(parentNode.NonExistingLines[i]); node.NonExistingLines.AddRange(parentNode.NonExistingLines); node.NonExistingLines.Remove(parentNode.NonExistingLines[i]); node.Boxes.AddRange(parentNode.Boxes); List <Box> newBoxes = AICommon.TryClosingBoxes(parentNode.ExistingLines, DeterminePlayerColour(node.Player), parentNode.NonExistingLines[i], out int[] notUsed); node.Boxes.AddRange(newBoxes); node.DeltaMove = parentNode.NonExistingLines[i]; #endregion /* don't go through this subtree if the delta move is drawing third edge * unless it the only move */ AICommon.TryClosingBoxes(node.ExistingLines, DeterminePlayerColour(node.Player), node.DeltaMove, out int[] surroundingEdges); if ((surroundingEdges[0] == 2 || surroundingEdges[1] == 2)) { if (!(i == parentNode.NonExistingLines.Count - 1 && parentNode.Children.Count == 0)) { Debug.WriteLine(node.DeltaMove.ToString() + " was thrown."); continue; } } node.EstimationScore = ConstructTree(node, depth + 1, MinimaxPlayerType.MIN, alpha, beta); bestValue = (bestValue > node.EstimationScore ? bestValue : node.EstimationScore); alpha = (alpha > bestValue ? alpha : bestValue); parentNode.Children.Add(node); if (beta <= alpha) { break; } } parentNode.EstimationScore = bestValue; return(bestValue); } else { int bestValue = int.MaxValue; for (int i = 0; i < parentNode.NonExistingLines.Count; i++) { #region New Node Creation MinimaxTreeNode node = new MinimaxTreeNode(); // calculate estimation function only for leaf nodes node.Player = (parentNode.Player == MinimaxPlayerType.MAX ? MinimaxPlayerType.MIN : MinimaxPlayerType.MAX); node.ExistingLines.AddRange(parentNode.ExistingLines); node.ExistingLines.Add(parentNode.NonExistingLines[i]); node.NonExistingLines.AddRange(parentNode.NonExistingLines); node.NonExistingLines.Remove(parentNode.NonExistingLines[i]); node.Boxes.AddRange(parentNode.Boxes); List <Box> newBoxes = AICommon.TryClosingBoxes(parentNode.ExistingLines, DeterminePlayerColour(node.Player), parentNode.NonExistingLines[i], out int[] notUsed); node.Boxes.AddRange(newBoxes); node.DeltaMove = parentNode.NonExistingLines[i]; #endregion /* don't go through this subtree if the delta move is drawing third edge * unless it the only move */ AICommon.TryClosingBoxes(node.ExistingLines, DeterminePlayerColour(node.Player), node.DeltaMove, out int[] surroundingEdges); if ((surroundingEdges[0] == 2 || surroundingEdges[1] == 2)) { if (!(i == parentNode.NonExistingLines.Count - 1 && parentNode.Children.Count == 0)) { Debug.WriteLine(node.DeltaMove.ToString() + " was thrown."); continue; } } node.EstimationScore = ConstructTree(node, depth + 1, MinimaxPlayerType.MAX, alpha, beta); bestValue = (bestValue < node.EstimationScore ? bestValue : node.EstimationScore); beta = (beta < bestValue ? beta : bestValue); parentNode.Children.Add(node); if (beta <= alpha) { break; } } parentNode.EstimationScore = bestValue; return(bestValue); } throw new Exception("Something wrong in minimax happened"); }