// MINIMAX FUNCTION public Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> > Minimax(GameBoard <counters> board, counters counter, int ply, Tuple <int, int> positions, bool mmax, GameBoard <int> scoreBoard, ref int cont, int alpha, int beta) { // decs counters us = Flip(counter); int ourindex = 1; List <Tuple <int, int> > availableMoves = getAvailableMoves(board, positions); // create new list of Tuple<int,int> List <Tuple <int, Tuple <int, int> > > result_list = new List <Tuple <int, Tuple <int, int> > >(); int bestScore = mmax ? -1001 : 1001; int score = Consts.MIN_SCORE; // current score of move Tuple <int, int> Move = new Tuple <int, int>(0, 0); Tuple <int, int> bestMove = new Tuple <int, int>(1, 1); // best move with score// THRESHOLD <============= // add assertion here // decs for random move Random rnd = new Random(); int randMoveX = rnd.Next(1, 7); // creates a number between 1 and 7 int randMoveY = rnd.Next(1, 7); // creates a number between 1 and 7 Tuple <int, int> randMove = new Tuple <int, int>(randMoveX, randMoveY); // check win if (availableMoves.Count == 0) { return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(10, positions, board, scoreBoard)); } else if (Win(board, counter)) { return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(1000, positions, board, scoreBoard)); } else if (Win(board, this.otherCounter)) { return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(-1000, positions, board, scoreBoard)); } if (FindTwoInARow(board, counter) && Two(board, counter) && board[Move.Item1, Move.Item2] == counters.EMPTY) { return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(100, positions, board, scoreBoard)); } else if (FindTwoInARow(board, this.otherCounter) && Two(board, this.otherCounter) && board[Move.Item1, Move.Item2] == counters.EMPTY) { return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(-100, positions, board, scoreBoard)); } else if (FindOneInARow(board, ourindex, this.otherCounter) && One(board, counter) && board[Move.Item1, Move.Item2] == counters.EMPTY) { return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(100, positions, board, scoreBoard)); } else if (FindOneInARow(board, ourindex, this.otherCounter) && One(board, this.otherCounter) && board[Move.Item1, Move.Item2] == counters.EMPTY) { return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(-100, positions, board, scoreBoard)); } // CHECK DEPTH else if (ply > maxPly) { score = EvalCurrentBoard(board, scoreBoard, ourindex, us); // call stat evaluation func - takes board and player and gives score to that player return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(score, positions, board, scoreBoard)); } // Console.WriteLine("HWL: Is this line reached?"); // Console.ReadLine(); // HWL: even with a ReadLine here, the program runs to the end // place random move if (board.IsEmpty()) // if board is empty then play random move { // decide scoring for random move score = EvalForWin(board, ourindex, us); // 1 for win, 0 for unknown // assign two score if (FindTwoInARow(board, us)) // player win? { score = 100; // twoinrow confirmed } if (FindTwoInARow(board, us + 1)) // twoinrow opponent? { score = -100; // twoinrow confirmed } if (FindOneInARow(board, ourindex, us)) // oneinrow? { score = 10; // oneinrow confirmed } if (FindOneInARow(board, ourindex, us + 1)) // oneinarow opponent? { score = -10; // oneinrow confirmed } // MakeRandomMove(board, counter, ply, positions, !mmax, scoreBoard, ref cont); // Console.ReadLine(); return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(score, positions, board, scoreBoard)); } GameBoard <counters> copy; // make copy original board copy = board.Clone(); // make copy board // copy.DisplayBoard(); // display copy board // cell priority - favour centre and corners for (int i = 0; i < availableMoves.Count; i++) { Move = availableMoves[i]; // current move // cell priority - favour centre and corners // HWL: where do you actual place the piece for the position in Move? you don't do this here, just pass Move to the call of Minimax below; in the recursive call you then overwrite the input argument with a random move (see comment at start of Minimax; so you are actually not considering Move at all! // HWL: try placing the piece here, and below just use the score copy[Move.Item1, Move.Item2] = counter; // place counter // GameBoard board0 = MakeMove(board, move); // copies board - parallel ready // ************************************************************************************************ // ************************************************************************************************ // ************************************** MAIN MINIMAX WORK *************************************** // ************************************************************************************************ // ************************************************************************************************ // list defined in Minimax declarations Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> > result = Minimax(copy, Flip(counter), ply + 1, Move, !mmax, scoreBoard, ref cont, alpha, beta); /* swap player */ // RECURSIVE call // trying to prevent preventing cell overwrite copy[Move.Item1, Move.Item2] = counters.EMPTY; /* counter; */ // HWL: remove counter that was tried in this iteration // GameBoard board0 = MakeMove(board, move); // copies board - parallel ready score = -result.Item1; // assign score positions = result.Item2; // present position (x,y) // list of all result - list of possible result Tuple<int, int> result_list.Add(new Tuple <int, Tuple <int, int> >(score, positions)); // assign score to correct cell in score scoreBoard[result.Item2.Item1, result.Item2.Item2] = score; // HWL: summarise the result of having tried Move, print the assoc scoreboard and check that the matching move is the one for the highest score on the board /* Console.WriteLine(mmax.ToString() + * " **HWL (ply={0}) Trying Move ({4},{5}) gives score {1} and position ({2},{3}) [[so far bestScore={6}, bestMove=({7},{8})", * ply, score, result.Item2.Item1, result.Item2.Item2, Move.Item1, Move.Item2, * bestScore, bestMove.Item1, bestMove.Item2); */ // scoreBoard.DisplayBoard(); if (score == Consts.MIN_SCORE || score == Consts.MAX_SCORE) { /* Console.WriteLine("**HWL CLAIM: putting piece {0} at position ({1},{2}) gives 3-in-a-row:", * counter, Move.Item1, Move.Item2); */ } // ************************************************************************************************ // ************************************************************************************************ // ******************************** END OF MAIN MINIMAX WORK ************************************** // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************* WHEN TO MAXIMISE, WHEN TO MINIMISE *********************************** // ******************************* WITH ALPHA-BETA PRUNING **************************************** // ************************************************************************************************ // ************************************************************************************************ // if maximising if (/* true HWL || */ mmax) { alpha = score; if (score > bestScore) { bestMove = Move; bestScore = score; } if (alpha > bestScore) { bestMove = Move; bestScore = alpha; } } // if minimising else { if (bestScore > score) { bestMove = Move; bestScore = score; } if (beta <= alpha) { bestScore = alpha; } return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(bestScore, positions, board, scoreBoard)); } // ************************************************************************************************ // ************************************************************************************************ // ********************* END OF WHEN TO MAXIMISE, WHEN TO MINIMISE ******************************** // ******************************* WITH ALPHA-BETA PRUNING **************************************** // ************************************************************************************************ cont++; // increment positions visited } return(new Tuple <int, Tuple <int, int>, GameBoard <counters>, GameBoard <int> >(bestScore, bestMove, board, scoreBoard)); // return }