6

I need some help with a problem. I recently got this in an interview and I was trying to solve it but having no luck. Here's the question:

Code an algorithm for a game consisting of two players. The input is a positive integer x. Each round, a player deducts a perfect square from the number. The player can choose any perfect square as long as it is less than or equal to the current number and greater than 0. The current player wins if the number becomes 0 after his deduction.

I suppose this should be done using dynamic programming/greedy approach which I am not very well verse with. Or we might need to come up with a sequence of winning/losing numbers and if a player ends up in a winning sequence, he will win no matter what. But how do we come up with such a sequence?

Can anyone help me solve this question possibly in Java?

UPDATE:

Solution 1 as suggested by DAle:

public int subtractPerfectSquares(int n)
   {
      if (n <= 0)
         return 1;
      boolean[] isWinningCase = new boolean[n + 1];

      for (int i = 1; i <= n; i++)
      {
         for (int num = 1; num * num <= i; num++)
         {
            if (!isWinningCase[i - (num * num)])
            {
               isWinningCase[i] = true;
               break;
            }
         }
      }

      return isWinningCase[n] ? 1 : 2;
   }

Solution2 modified for better understanding:

 public int subtractPerfectSquares2(int n)
   {
      if (n <= 0)
         return 1;
      boolean[] isWinningCase = new boolean[n + 1];

      // if we reach 0, we win
      isWinningCase[0] = true;
      // 1 is a win 
      isWinningCase[1] = true;
      // 2 is a losing condition. We must define this as this state dictates losing scenarios for further states
      isWinningCase[2] = false;

      // we start from 3
      for (int i = 3; i <= n; i++)
      {
         for (int num = 1; num * num <= i; num++)
         {
            int prefectSquare = num * num;
            // if we get to 0 from current number or if we get to a losing scenario (to be faced by next player) from current number, then the current state is a winning position
            if (i - prefectSquare == 0 || !isWinningCase[i - prefectSquare])
            {
               isWinningCase[i] = true;
               break;
            }
         }
      }

      // return player 1 if given number is a winning state else player 2 wins
      return isWinningCase[n] ? 1 : 2;
   }
Thomas99
  • 71
  • 1
  • 5
  • Is the algorithm just for the game itself and error checking for numbers that are not perfect squares or that are greater than the current number value? Or do you need to create the players to play the game as well? – theawesometrey Jun 19 '19 at 01:32
  • 1
    @TreyGraham The algo is just for the game. We do not need to create the players. The players can be represented just by turns (odd turn and even turn). Each time the current player OR lets say for simplicity, in the action in the current turn., we MUST subtract a perfect square which is lesser or equal to the current number. If the number becomes 0, player in current turn wins. – Thomas99 Jun 19 '19 at 03:13
  • Please check the answer which i've given. really appreciate your comments@Thomas99 – Lahiru Wijesekara Jun 19 '19 at 07:59

2 Answers2

4

The only difference between players in this game is that player 1 goes first. This type of games is called impartial game and the perfect strategies of both players are identical. Furthermore, we can divide all states (integer x) of the game into two types: winning position or losing position using the following rules:

  1. x=0 is a losing position
  2. position x is a winning position if there is at least one possible move that leads to a losing position.
  3. position x is a losing position if every possible move leads to a winning position.

Then we need to determine the type of all positions from 1 to x. Possible implementation:

boolean[] isWinning = new boolean[x+1];
for (int state = 0; state <= x; ++state) {
    isWinning[state] = false;
    for (int i = 1; i*i <= state; ++i) {
        int perfectSquare = i*i;
        if (!isWinning[state - perfectSquare]) {
            isWinning[state] = true;
            break;
        }
    }
}

If the current player is in the winning position (isWinning[x] == true), you should choose such perfect square that isWinning[x - perfectSquare] == false. This will bring the game (and another player) to a losing position. If the player is in the losing position, nothing could save him, every possible perfect square is equally bad.

DAle
  • 8,990
  • 2
  • 26
  • 45
  • That worked and I was confused of why the state 0 is a losing state here. I suppose just to support your logic that if we find atleast one losing state the current state is a winner. So, instead of that, can we put state 1 as a winning state and begin our game from 2? – Thomas99 Jun 19 '19 at 16:39
  • I've added two versions of solutions in my question. Please do check my solution and suggest me any changes – Thomas99 Jun 19 '19 at 16:53
  • @Thomas99 Yes, zero state is for convenience (but it is actually a real terminal position for a losing player). If you want to avoid this state, you need to mark all perfect square positions as winning position, not only x=1. – DAle Jun 19 '19 at 16:58
0

Below program will help you to implement the logic. I've tried implementing the code as per your requirement.Make sure to comment if you need improvements or to clarify any misunderstandings of the logic.

public class Game {

public static void main(String[] args) {
    int number = 0;
    int count = 1;
    int player = 1;
    System.out.println("Please Enter a positive integer");
    try (Scanner sc = new Scanner(System.in);) {
        number = sc.nextInt();
        while (number > 0) {
            int numberArray[] = generatePerfectSquare(1, number);
            System.out.println("===================");
            System.out.println("perfect square numbers");
            for (int i : numberArray) {
                System.out.print(i);
                System.out.print("   ");
            }
            System.out.println("");
            System.out.println("===================");

            player = ((count % 2 == 0) ? 2 : 1);
            System.out.println("Round : " + count + "   Player : " + player);
            System.out.println("Please Enter your prefered perfect square number");

            number = number - sc.nextInt();
            if (number <= 0) {
                System.out.println("****************");
                System.out.println("You won the game");
                System.out.println("===================");
            } else {
                System.out.println("===================");
                System.out.println("You should try more");
                System.out.println("===================");
            }
            count++;
        }
    } catch (Exception e) {
        System.out.println(e);
    }

}

private static int[] generatePerfectSquare(int start, int end) {

    if (start > end || start < 0) {
        throw new IllegalArgumentException();
    }
    int[] perfectSquares = new int[end - start];
    int n = 0;
    int candidate = (int) Math.ceil(Math.sqrt(start));
    int square;
    while ((square = candidate * candidate) < end) {
        perfectSquares[n++] = square;
        candidate++;
    }
    return Arrays.copyOf(perfectSquares, n);
}

Test outcome

enter image description here

Lahiru Wijesekara
  • 623
  • 1
  • 10
  • 22
  • Thanks for the comment. But, I was not clear in my question and I apologize for that. We shouldn't take the perfect square inputs from the user. The computer itself must play with two players starting from player 1 and in the current player's action, the algo must greedily must choose a perfect square such that it reaches to 0 or reaches to a number where the opposition player cannot win. – Thomas99 Jun 19 '19 at 16:38