Module 2: Developing a game


Objectives

 

By the end of this module, you will:

 

NOTE:

 

2.0 Audio:
 


Step 1: Build some useful methods

 

As a first step, you will write some methods that will be used repeatedly in the game.

The class MyGameArrayTool.java will feature these methods:

    public static int[] shiftedArray (int N)
    {
	// This needs to return an array with 1,2,...,N*N as the values.
	// INSERT YOUR CODE HERE
    }

    public static void shuffle (int[] numbers)
    {
	// This needs to shuffle the numbers.
	// INSERT YOUR CODE HERE
    }

    public static void putNumbersOnBoard (int[][] board, int[] numbers)
    {
	// This needs put the numbers in the second array (M of them)
	// on the N*N board. M=N*N. However, it needs to put them
	// in Cartesian order going left to right starting at the bottom.
	// INSERT YOUR CODE HERE
    }

    public static boolean isPrime (int n)
    {
	// Is n a prime number?
	// INSERT YOUR CODE HERE.
    }


    public static void printBoard (int[][] board)
    {
	// INSERT YOUR CODE HERE for a Cartesian print
    }
     

A few more details:

 

2.1 Exercise: Download MyGameArrayTool.java and write your code in that file. Compile and run the tests to make sure your implementations are working.
 

2.2 Audio:
 


Step 2: Learn about the game by playing

 

Let's start with a brief description:

  • The game is called Prime.
           ⇒ It will involve our favorite kind of number: prime numbers.

  • It's a 2-person board game with each person taking turns.

  • The only choice made at each turn is whether to move "down" or "horizontally" by one cell, or to stay in place.
    • Player A can only move right when moving horizontally.
    • Player B can only move left when moving horizontally.
    • A player cannot move out of bounds, nor can move into a cell occupied by another player. Either such move results in immediately losing the game.

  • After the move, the player's score is updated based on the cell that the player chose to move to.

    • A cell with a prime number adds a multiple of that number to the player's score.
      • For example if the multiplier is 10, landing on a prime cell with the number 7, results in 70 being added to your score.
    • Otherwise the cell's number is subtracted from the player's score.
      • For example, landing on a cell with the number 42, results in 42 being subtracted from your score.

  • Obviously, one should try to get to the prime cells by going through as few of the other cells as possible.

  • Also because one must move down or horizontally, there are limited number of moves (2N-1) in a game.

  • And that's it as far as the rules are concerned.
 

Your second programming goal (which will be explained below in more detail) will be to write code for a strategy for each of two players:

  • You will write code for PlayerA in PlayerA.java in which your code decides how to move.

  • You will write code for PlayerB in PlayerB.java in which your code decides how to move.
 

The game can be played in one of 7 types of competitions:

  1. Game type 1: your PlayerA plays your PlayerB (software vs. software).

  2. Game type 2: RandomA vs Manual. Here you can manually play (as a B-player) against a strategy that moves randomly (as an A-player).

  3. Game type 3: You can manually (as B) play your code strategy (playing as A).

  4. Game type 4: Your playerA plays RandomB. A minimal expectation for your strategy is to consistently beat RandomB.

  5. Game type 5: RandomA vs RandomB. This is just to test that it's all working after you've written your GameArrayTool code. Or to while away an afternoon watching random players against each other.

  6. Game type 6: Your playerA against OptimusPrimeB. The latter is a more careful strategy aimed at winning. We would be impressed if your playerA consistently beats OP.

  7. Game type 7: You can manually (as A) play against OptimusPrimeB.
Note: because the numbers are randomly distributed, a player can get lucky. However, over many games, a good strategy can beat a poor one.
 

2.3 Exercise: Download primegame.zip. However, do not unzip by clicking on the zip file. Instead, un-zip the file at the commandline in Terminal. To do that, enter the directory where the zip file is, and type unzip primegame.zip. This will make a directory called primegamezip. Enter that directory and compile the file PrimeGame.java. Then, at the command line of terminal type java PrimeGame 5. This will start off a game of type 5 (random vs random).
 

Some observations and next steps:

  • Notice that, as the game is executing, the initial board configuration and each player's subsequent moves are being printed to the terminal.

  • The game uses nothing more complex than DrawTool, which is itself a very basic drawing program.

  • If you run the game repeatedly, you will notice that the numbers 1,2,...,100 are not randomly spread around the grid. This is deliberate: the numbers are fixed to allow predictability in testing out your code. You can then make a small adjustment to make a random board each time (we'll do this later).

  • So far we've not used your code in MyGameArrayTool. That will come later.

  • Notice that as we start to develop more complex programs and applications, multiple Java files will be involved. Examine the number of Java files in the directory by typing ls *.java.
 

2.4 Audio:
 


Step 3: Write your strategies

 

Things to keep in mind:

  • You will write a player-A strategy in the move() method in the file PlayerA.java.

  • At each step of the game, when it's A's turn, the game calls the move() method in your PlayerA.java to determine what your code decides as its next move.

  • Observe that the return type is char.

  • All you have to do in the method is to decide whether to return 'd', 'h', or 's'.

  • Examine the file RandomA.java. This is a silly strategy but it will help get you started by showing you, for example, how to avoid breaking the rules.

  • Clearly, your strategy will make use of where you happen to be on the board each time it's your move. This information is given to you as parameters to the move() method.
 

Suggestions for proceeding:

  • First copy over the code from RandomA.java and compile. Then run the game with gametype=3 so that you manually play this strategy.

  • Write your own strategy by replacing the code and continue playing manually.

  • When your strategy is sufficiently refined, use gametype=4 to play against RandomB and confirm that you consistently win.

  • Use random boards to further refine:
    • Open the file PrimeGame.java and use control-w to search for shuffle in the file.
    • You will see that the line containing that has been commented out.
    • Un-comment the line by removing the comment slashes.
    • This will have the effect of randomizing the board each time.

  • Once you've done your PlayerA, work on a different idea for PlayerB.
 

2.5 Exercise: Implement your PlayerA and PlayerB strategies. Play them against each other, against Random, and against OptimusPrime. Find other students to play against. How did your approach fare?
 

2.6 Audio:
 


Step 4: Use your arraytool

 

While developing your strategy, the toolbox used was GameArrayTool.

The source code for this was not included so that you can develop your version.

To use your version:

  • Use control-w to search for GameArrayTool in the file PrimeGame.java.

  • Replace each occurrence of GameArrayTool with MyGameArrayTool so that your methods are called.
 

2.7 Exercise: Use your MyGameArrayTool for the game.
 

  • Assuming your implementations worked, the game should function as before.

  • Congratulations! You've helped build a game.
 


Step 5: Examine a complex strategy

 

2.8 Exercise: Open the file OptimusPrimeB.java in your editor and cursorily examine the code.
 

About this strategy:

  • It should be quite incomprehensible.

  • In a typical CS curriculum, an Algorithms course is often the 4th or 5th course, long after programming. And towards the end of such a course is when a technique called dynamic programming is taught. It is difficult to understand and apply.

  • It just so happened that this algorithmic approach is well-suited to this game.

  • We do NOT expect beginning students to understand how it works.
 

About game strategies:

  • Writing strategies for games is a subfield in itself, and has a long history.

  • There are games like tic-tac-toe for which optimal strategies can be computed, but others like chess in which brute computing power must be combined with strategies to win.
 


Step 6: Examine the game code

 

2.9 Exercise: Open the file PrimeGame.java in your editor and carefully examine the code.
 

Let's walk through the design:

  • First, let's take a look at the key global variables:
        static int N = 10;                         // Grid size N×N
        static int[][] board = new int [N][N];     // board[i][j] == a number
        static int[][] points = new int [N][N];    // the points won/lost at a cell
        static int primePointMultiplier = 10;      // 10*prime# for positive points
    
        // Current locations of the two players:
        static int playerAX=0, playerAY=N-1;
        static int playerBX=N-1, playerBY=N-1;
    
        // Current scores:
        static int playerApoints=0, playerBpoints=0;
         
  • Thus the main idea of the whole program is:
         Do the initialization: 
             Make the board, draw the board, initalize locations
    
         while (! over) {
             move player A
             move player B
         }
    
         print points and game result
         
  • This is essentially what's in the method executeGame() (the initialization is done in methods called from main()):
        static void executeGame ()
        {
    	boolean over = false;
    	boolean canMoveA = true, canMoveB = true;
    	int numSteps = 0;
    
    	while (! over) {
    
    	    numSteps++;
    
    	    canMoveA = checkCanMoveA ();     // Need to see if A can move.
    
    	    if (canMoveA) {                  // A can move.
    		boolean isLegal = moveA ();  // A's move worked.
    		if (! isLegal) {
    		    playerApoints = -1;      // If not, A loses.
    		    break;                   // End the game right away.
    		}
    	    }
     
                // ... similar code for player B ...
    
             }
         
  • Player A is "stuck" if it's on the lowest row and rightmost cell:
        static boolean checkCanMoveA ()
        {
    	// You can't move A if it's on the lowest level and x >= N.
    	return ( ! ((playerAY == 0) && (playerAX >= N-1)) );
        }
         
  • What remains is to actually arrange for either a manual move or to call the appropriate program for a move. Here's some of the code in moveA():
        static boolean moveA ()
        {
    	char c = ' ';                     // Default (to catch errors).
    	int[][] b = copyArray (board);    // Make a copy to prevent tampering.
    	if (gameType == 1) {              // PlayerA vs Player B.
        	    sleep (sleepTime);
    	    c = PlayerA.move (b, playerAX, playerAY, playerBX, playerBY);
    	}
    	else if (gameType == 2) {
    	    // ... etc .. for all the game types ...
    	}
    
            // Evaluate legality of move and then actually change
            // playerAX, playerAY, and adjust points.
            // ...
        }
         
  • The code for player B is nearly identical.

  • There are also some supporting methods like copying the board. Why copy the board? We send a copy to each player in case a player's strategy code inadvertently writes over the board.

  • Finally, let's take a look at the initialization code:
        static void makeBoard ()
        {
    	// First make an array of numbers 1 .. M=N*N
    	int[] numbers = GameArrayTool.shiftedArray (N);
    
    	// Next, place numbers on the board.
    	GameArrayTool.putNumbersOnBoard (board, numbers);
    
    	System.out.println ("Initial board:");
    	GameArrayTool.printBoard (board);
    
    	// Now do points. 
    	for (int y=0; y<N; y++) {
    	    for (int x=0; x<N; x++) {
    		if (GameArrayTool.isPrime(board[x][y])) {
    		    points[x][y] = board[x][y] * primePointMultiplier;
    		}
    		else {
    		    points[x][y] = -board[x][y];
    		}
    	    }
    	}
        }
         
    But you already know this code.
 

Ready to build your own game?

  • There are simple games and puzzles without much by way of graphics or storytelling.
           ⇒ These are easy for a single person to develop.

  • A full-fledged video game, on the other hand, needs a large team:
    • Graphic artists to design and create imagery.
    • Game engine software developers to create the logic of the game.
    • Software developers to handle communication, servers and such for multi-player online games.
    • Software developers for interfacing with game hardware.

  • Employment in the game industry is a popular choice for students but it's highly competitive.

  • To secure a future in the game industry, it's probably helpful to do a few things:
    • Take courses in fine arts so you learn how to use tools for digital arts and animation.
    • Get a Master's degree in game development and computer graphics, which will certainly cover the above and typically position you for both the game and animation industries.
    • Build some games because that's how you open doors for interviews.
    • Seek an internship in a game development company.
    (And let's not forget ... complete the GatewayToCS program.)
 


Meta

 

Let's step back and point out a few things:

  • Not only was the application more complicated than anything we've seen before, but also the folder had many files, not all of which were essential to understanding.

  • Even running the application was complicated by the variety of options (the 7 types of playing configurations).

  • When faced with such complexity, one can take a top-down or bottom-up approach.

  • We recommend doing a bit of both simultaneously:
    • Start top-down by understanding the game and its rules.
    • Then understand what happens in main().
    • Get a feel for the different parts by skimming through.
    • Trace through the code once.
    • Then understand methods at the high-level: what goes in, and what comes out.
    You do NOT need to understand everything to be able to get the job done.

  • One of the hardest tasks in software development is to understand someone else's code and why it was designed the way you see it.

  • As we've said before: the more you do this, the better you will get at it.
 

2.10 Audio:


On to Module 3



© 2017, Rahul Simha