예제 #1
0
    //calculates the modular sum of each bool[] in nums which represents a number in binary
    public static bool[] CalcSum(bool[][] nums)
    {
        if (nums.Length == 1)
        {
            return(nums[0]);
        }

        bool[] total = nums[0];
        for (int i = 1; i < nums.Length; i++)
        {
            total = PathCutthroat.ModAdd(total, nums[i]);
        }

        return(total);
    }
예제 #2
0
    //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);
    }
예제 #3
0
    //calculates the modular sum of each bool[] in nums except for nums[notIndex]
    public static bool[] CalcSumOthers(bool[][] nums, int notIndex)
    {
        if (nums.Length == 1 && notIndex == 0)
        {
            return(new bool[1]);
        }

        bool[] total = (notIndex != 0) ? nums[0] : nums[1];
        for (int i = ((notIndex != 0) ? 1 : 2); i < nums.Length; i++)
        {
            if (i != notIndex)
            {
                total = PathCutthroat.ModAdd(total, nums[i]);
            }
        }

        return(total);
    }
예제 #4
0
    public static void Main()
    {
        //display instructions
        Console.Out.WriteLine("Welcome to the game of Path Cutthroat!\nYou will take turns with the computer removing vertices and their edges from a path.\nIf a vertex loses all of its neighbors, then that vertex will dissapear too.\nThe player to remove the last vertex wins!");
        //retrieve game size (in valid format)
        int  size = 0;
        bool number;

        do
        {
            number = true;
            Console.Out.Write("\nHow many vertices will you play with? ");
            try
            {
                size = Convert.ToInt32(Console.ReadLine());
            }
            catch (FormatException e)
            {
                number = false;
            }
        } while (!number || size < 1);

        //initialize grunds as is done in PathCutthroat
        grunds = PathCutthroat.CalcGrunds(size);

        //initialize pieces with a Path the size of the game
        pieces = new List <Path>();
        pieces.Add(new Path(1, size));

        InitDisplay(size);

        //playerTurn true if it is the player's turn, this will be used to determine who wins
        bool playerTurn = true;

        //while the game is not over
        while (pieces.Count > 0)
        {
            playerTurn = true;

            //retrieve the player's move (in valid format)
            int move = 0;
            do
            {
                number = true;
                Console.Out.Write("Which vertex will you remove? ");
                try
                {
                    move = Convert.ToInt32(Console.ReadLine());
                }
                catch (FormatException e)
                {
                    number = false;
                }
            } while (!number || move < 1 || move > size || !display[2 * move - 2]); //true only when the player's input is invalid


            Turn(move);

            //if the game is over, break
            if (pieces.Count == 0)
            {
                break;
            }

            //computer's turn
            playerTurn = false;
            move       = CalcMove();
            Console.Out.WriteLine("The computer removes vertex " + move + ".");
            Turn(move);
        }
        //write end message
        Console.Out.WriteLine((playerTurn) ? "Congratulations! You played perfectly." : "Sorry, you lost.");
        Console.ReadLine();
    }
예제 #5
0
    //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);
    }