//finds the maximum number in nums where each bool[] in nums represents a binary number public static int FindMax(bool[][] nums) { int index = 0; int num = PathCutthroat.FromBinArray(nums[index]); for (int i = 1; i < nums.Length; i++) { int numCheck = PathCutthroat.FromBinArray(nums[i]); if (num < numCheck) { index = i; num = numCheck; } } return(index); }
//calculate a winning move if one exists, otherwise just choose the first vertex of the first piece in pieces public static int CalcMove() { //piles represents the grundy numbers of the sizes of each Path in pieces, in binary bool[][] piles = new bool[pieces.Count][]; for (int i = 0; i < pieces.Count; i++) { piles[i] = PathCutthroat.ToBinArray(grunds[pieces[i].Size()]); } //totalSum is the sum of all numbers in piles without carry-over bool[] totalSum = CalcSum(piles); //endAt is the largest index in totalSum to hold a 1 (true) int endAt; for (endAt = totalSum.Length - 1; endAt >= 0; endAt--) { if (totalSum[endAt] == true) { break; } } //endAt == -1 only if there is no winning move (i.e. totalSum == 0) and so return the first vertex of the first piece in pieces if (endAt == -1) { return(pieces[0].Front()); } //coorPiles stores the binary numbers in piles truncated at the index endAt bool[][] coorPiles = new bool[pieces.Count][]; for (int i = 0; i < pieces.Count; i++) { bool[] p = piles[i]; coorPiles[i] = new bool[Math.Min(endAt + 1, p.Length)]; for (int j = 0; j < p.Length && j <= endAt; j++) { coorPiles[i][j] = p[j]; } } //the optimal move is to remove from the Path that corresponds to the max number in coorPiles int targetIndex = FindMax(coorPiles); //the optimal move is to make the targetIndex Path have the grundy number of the sum of all the other Paths (without carry-over) //first calculate what this sum is for the truncated numbers (coorPiles) since it is already equal to the sum for all places above since they were zero int lowerTargetVal = PathCutthroat.FromBinArray(CalcSumOthers(coorPiles, targetIndex)); //diff stores the change that needs to happen to pieces[targetIndex] in terms of grundy numbers int diff = PathCutthroat.FromBinArray(coorPiles[targetIndex]) - lowerTargetVal; //targetVal then stores the grundy number that pieces[targetVal] must have (as an optimal move) int targetVal = PathCutthroat.FromBinArray(piles[targetIndex]) - diff; //Need to split pieces[targetIndex] so that the two paths sum to targetVal in grundy value //splitter will store the index of what needs to be removed in pieces[targetValue] where 0 is the front int splitter = 0; int n = pieces[targetIndex].Size(); for (int i = 0; i < n / 2 + 1; i++) { //when i is found such that it splits pieces[targetIndex] into two piles equivalent to a nim pile of targetVal, set splitter to i and break if (PathCutthroat.ModAdd(grunds[i], grunds[n - i - 1]) == targetVal) { splitter = i; break; } } //splitter stores how far into pieces[targetIndex] the vertex to be removed is return(pieces[targetIndex].Front() + splitter); }