Posted: February 27th, 2023
Java programming need to develop game Play Tetris
Due Date: March 3
Need full solution: Problem Coverage 100% and 100% code path test coverage
Please feel free to improve and resubmit your code as many times as you like (before the deadline) in order to improve your score.
Overview
TetrisLinks to an external site. was arguably the most popular single-player computer game of all time by the early 1990’s. It’s incredibly easy to learn, and even to this day it’s widely considered to be among the best video games of all time.
There are some animations in the link above, but in case you’ve never seen it, the interface consists of a tall narrow rectangular grid as the game board. Randomly-selected geometric shapes appear at the top, one at a time, and begin to incrementally fall from top to bottom. The player must manipulate each shape as it falls, by moving it horizontally within the board and/or rotating it in 90° increments. Once the shape can fall no further, it becomes locked in place, taking up some of the available spaces within the board.
The goal is to continue playing as long as possible, so the player must attempt to place the shapes strategically. Completing horizontal rows is crucial to prolonging the game. If the player can form one or more horizontal rows completely full of blocks, those rows are removed, with all blocks above falling downward. Eventually, the board fills upward to a limit line, and if any portion of a shape is locked in place above this line, the game is over.
You’ll see all of this in action soon enough, and it’s usually quite easy to understand once you’ve played it for just a few minutes.
Requirements
Building a full-functional Tetris application requires a little more Java than what we’ve covered yet, so you’ll be provided a working application that you can download and play. It’s a simplified version of the game, with a very rudimentary scoring mechanism, but it’s still quite enjoyable and challenging.
You may wonder, if the application is provided, what this is assignment all about. The goal here is to develop a simple Artificial Intelligence (“AI”) system, capable of playing the game as if it were the human player. The game system you’ll download can ask your AI what to do, just as each shape first appears at the top of the board. The game will provide your AI the identity of the shape, along with the current contents of the board. Your AI will then compute the best of all possible placements, using a cost-based algorithm, and provide it to the game.
Implementing an Interface
In previous assignments, you were given a class file with method declarations, Javadoc comments, and placeholder implementations. In this assignment, you’ll be given an interface instead. An interface contains the method declarations and the Javadoc comments, but no code at all (not even placeholders). Your task is to implement this interface, which means you’ll develop a class that provides code for all the methods declared in the interface. In addition to the methods specified by the interface, you will likely need to develop several private helper methods in order to fully implement it without redundancies.
System Description
The interface you will implement is called edu.vt.cs5044.tetris.AI and your implementation class must be called edu.vt.cs5044.TetrisAI. You will need to develop the code needed to implement the methods defined by the interface. One of the methods will be the AI system, responsible for choosing the best placement for each shape. The other methods will support this by performing necessary calculations. These support methods would normally be private, and not specified by the interface, but for academic purposes we’re exposing these methods to ease the testing requirements. Speaking of tests, you must create a formal JUnit test file, called edu.vt.cs5044.TetrisAITest.
Downloads
tetris5044.jar Download tetris5044.jarlibrary file containing all the compiled game engine classes tetris5044-api.jar Download tetris5044-api.jarlibrary file containing the Javadocs for the game engine
Setting up Eclipse
Download all the files from the links above to your computer, placing them in any convenient folder. Your Eclipse workspace is fine, as long as it’s not within any project folder. Open Eclipse and create a new Java Project for this assignment. As with all our projects, don’t create the module-info file, if prompted.
Right-click the project and select Build Path | Configure Build Path, then select the Libraries tab. Select “Classpath” then click “Add External JARs” and navigate to where you placed the downloaded JAR files, select tetris5044.jar, and click OK. You should now see that file listed in the Classpath section (along with the JRE System Library in the Modulepath section).
Now click the little triangle to expand the new library, select the node called Javadoc location: (None), and click the “Edit…” button. Here be sure to select “Javadoc in archive” first, then click Browse. Navigate again, this time selecting the tetris5044-api.jar file, and click the “Validate” button. It should tell you the location is “likely valid” so click OK, then OK, to get back to the Libraries tab.
Now select “Classpath” again, then click the “Add Library…” button. Select “JUnit” from the list, then select “JUnit 4” (note: this version is not the default!) and click Finish.
Ensure you see all these libraries now:
· Modulepath
· JRE System Library [JavaSE-xx]
· Classpath
· tetris5044.jar
· Javadoc location: [path to tetris5044-api.jar]
· JUnit 4
Note that it’s perfectly fine if these appear in a different order within each category, as long as they’re all listed in the correct categories. Click “Apply and Close” to accept this build path and return to your project.
Right-click your project name, select New | Source Folder. (If “Source Folder” isn’t listed as an option, select “Other” and find it in the “Java” section.) Enter the name as “test” and you should see it listed in your project, as a peer to the “src” source folder. Next, add a New | Java Package called “edu.vt.cs5044” within each of these source folders (meaning both “src” and “test” should have the same package).
Shall We Play a Game?
At this point, you can right-click the project name, and select the Run As | Java Application. Several classes will appear, and you should see Tetris5044 – edu.vt.cs5044.tetris listed near the top. However, you may need to type the first few letters to search for this. Select this class, and click Run to play Tetris!
A game window should appear with an empty game board and a prompt to type ‘P’ to play. Go ahead and try this now, to see the game in action.
NOTE: If the application appears to launch, but the game window doesn’t appear, please ask in Piazza for help. Make sure to mention which OS you’re using. Some OS configurations may require a few additional settings.
Once you type P, you will see a new random shape near the top, slowly falling toward the bottom of the well. There are several alternative keyboard controls you can use, but for now just type ‘A’ and ‘D’ to move the shape left and right, and type ‘W’ to rotate the shape. Once the shape is situated as you wish, you can use ‘S’ (or the space bar) to drop the shape immediately, or you can just enjoy watching the shape fall gently into place. The shape will change color once it’s locked into the board, and can no longer be manipulated.
Notice in the console there is a Welcome message. You can type ‘?’ into the game (click the game screen before doing so, to ensure the game receives the keystroke) at any time to produce a listing in the Eclipse console of all of the available options and keyboard controls. Try this now. Some of these features will be extremely useful during the assignment. You’ll probably notice some interesting options, including a way to enable the Player AI by typing Ctrl-P. When you enable this mode, just before each new shape appears in the game, the AI is asked how to best rotate then position the shape.
Try actually typing Ctrl-P into the game window. If you’re in a game, it will end, and you should see a text warning in the console that your AI implementation couldn’t be found. That’s perfectly understandable at this point, because we haven’t created the AI yet.
Back To Work
Close the game for a moment now, while we get to our coding tasks for a while. First, right-click the edu.vt.cs5044 package of the “src” source folder, and select New | Class.
Before you enter the class name, click the “Add…” button to the right of the Interfaces section. In the search box at the top, type AI and you should soon see the AI interface (in the edu.vt.cs5044.tetris package) selected. Click Ok to add that interface. For now, un-check the option to create “Inherited abstract methods” (if checked) and then enter the class name as TetrisAI and click Finish. Notice that the new file declaration says public class TetrisAI implements AI. This is what tells Java that we intend to be compatible with any system that can work with the AI interface. You’ll also notice Eclipse is already showing an error on the class declaration line. Click the tiny red X in the margin and double-click “Add unimplemented methods” from the pop-up. This asks Eclipse to generate all the method placeholders for you! (The option you un-checked earlier would have done the same thing; this way you’re able to see exactly what it’s doing.) Save the file, and review your new code. It’s already compatible with the game system, even though all it does is return placeholders. That obviously won’t meet our requirements, but it’s actually plenty to get started. Let’s check whether it’s actually recognized by the game engine.
Launch the application again, and type Ctrl-P to activate the Player AI. Confirm that the console says the Player Mode is now AI. If so, go ahead and type P to start the game.
Well, that’s not very exciting. The console just keeps printing warnings that the AI has selected a null move (which is invalid) for each shape, so we still need to manually place the shapes. That’s fair enough, since all we have are placeholders that Eclipse generated for us, but at least we know the game successfully found our AI, and that it seems to be communicating with it. Close the game again, so we can code a little more.
NOTE: You must close the game and restart it, each time you make any source code changes.
Not Very Deep Thoughts
Let’s at least provide a valid suggestion, even if it’s always exactly the same suggestion. In the findBestPlacement() method, let’s replace the placeholder: return null;
with this:
return new Placement(Rotation.NONE, 0);
Eclipse may complain that it doesn’t know about the Rotation class yet; if so, just double-click the tiny error icon to import these classes from the “edu.vt.cs5044.tetris” package. That should resolve the error, so save your file again. This really doesn’t seem like much of an improvement, but we’ll try it anyway.
Launch the game and use Ctrl-P then P to see what happens. Notice that it’s placing everything against the left edge (column 0) without any rotation. Also notice that the falling shapes are now a different color, confirming that a valid placement was provided by the AI. Shapes can’t be manipulated by the keyboard in this state.
We’ve taken a very important step here. Our AI is actually playing the game, and placing the shapes automatically for us! Right now, the decisions aren’t very good at all, but things can only get better from here. Close the game once you’re ready to code again.
Read All About It
Obviously we need to examine the shape and the state of the board in order to make some reasonable decisions. Let’s take a look at all those Javadocs that make up the API. In your source file click within the word AI of “implements AI” and type Shift-F2 (Fn + Shift + F2 on Mac). This should open the Javadocs for the AI interface using an internal browser within Eclipse. You can set it to use an external browser, if you prefer, but this is fine for now.
Browse through the Javadocs, to get familiar with the interface, and the classes you’ll be using to implement it. For example, click the findBestPlacement() method of the interface, and you’ll see it actually provides a brief outline of a strategy we can use. Click the “Package” link at the very top of any Javadoc page to see the package overview, then navigate throughout the various pages to see what each class and enum can do, along with the requirements for the interface. A fundamental part of this assignment is to learn how to use Javadocs to explore new libraries, as well as to see how a somewhat larger scale system is divided into multiple classes, each with its own purpose.
NOTE: The class Tetris5044 is for internal use only; you won’t need it at all. Also, classes RandomMode and ShapeStream are only needed for the optional “challenge” section below.
Strategy Games
There’s no such thing as a perfect Tetris strategy, so we won’t even try. We are necessarily taking the heuristicLinks to an external site. approach to this problem, and there will necessarily be trade-offs involved. In every placement decision, there are advantages and disadvantages. How can we hope to find anything approaching a “best” placement? Now is probably a good time to look at those other methods you’ll need to implement in the AI interface.
Development Phase 1: Cost Methods
From the API, the remaining methods of the interface are related to making some measurements to evaluate a board position. The idea here is that we’ll compute four distinct “cost” factors, which can measure any arbitrary board. We’ll eventually use these to determine how bad (or good) a particular placement decision might be. We’ll need to develop these cost methods first, before we can hope to have a working AI system.
We’re focusing on TDD in this project, so start with some fairly straightforward test cases to ensure that each individual cost calculation method works. Just construct a test board object, then assert the cost value you expect your implementation to compute.
Please be sure to avoid nested loops in your cost method. This isn’t a specific requirement, and won’t directly reduce your score, but nesting can cause a lot of confusion and wasted time. Also, nesting in these methods will tend to introduce a lot of redundancies, and redundancies will reduce your score!
Important: Although it isn’t strictly necessary to fully complete Phase 1 before beginning Phase 2, it is most highly recommended that you do so. Phase 2 will be extremely difficult to troubleshoot without a properly functional Phase 1.
Development Phase 2: Finding the Best
Once all of your individual cost calculation methods are working exactly as expected, it’s time to put it all together. The findBestPlacement() method will need to iterate through every possible placement of the shape, and determine the board that would result from each placement. It will then measure each resultant board by computing the weighted sum of the cost factors, then choose the placement that resulted in the lowest cost board. That placement will be returned to the game engine.
Unlike Phase 1, here your implementation is expected — but not required — to involve nested loops. There’s no redundancy involved, and it’s a natural solution to this particular problem. Using nesting here will actually reduce the overall complexity of your code.
The weightings noted above are important, because certain cost factors should have more influence on the overall decision than others. At first you can just set all the weights to 1, by simply summing the individual cost factors together. Eventually, once it’s working as expected with equal weights, we’ll need to adjust the weights to achieve better results.
Strategic Limitations
Luck — in the form of randomness — plays a starring role in Tetris. Even with the best of strategies, it’s critical to recognize that some games simply provide a more fortunate sequence of shapes than others. Thus it can be difficult to objectively judge exactly how good a particular strategy might be. The same strategies that do very well in some games may do very poorly in others. To help with this, our implementation provides a set of 4 repeatable “TEST” sequences we can use as a benchmark. The average number of shapes placed from these TEST sequences will act as a very reasonable metric of how well our strategy is working overall.
Teaching to the Test
Eventually we’ll be making a linear combination of our four costs, which just means we multiply each cost value by some weight, then add the weighted costs. The equation is very straightforward:
overallCost = weight1*cost1 + weight2*cost2 + weight3*cost3 + weight4*cost4
Computing the individual costs is described in the API, but what about the weights? As noted above, we can start by simply setting all the weights to 1, and indeed that strategy should place exactly 70.25 shapes, averaged over the four provided TEST sequences. Note that the sequence in which you iterate, and how you handle ties, can affect the outcome. If you’re getting something close, but not exact, please ask in Piazza.
Standards of Learning
Can we do better? Sure! As a start, reasonable weight ranges for the selected factors will be 0 through 12, in increments of 4, meaning you should use combinations of weight values of 0, 4, 8, and 12 for each cost factor. You can just use trial-and-error if you wish; watch your AI play the test games and try to figure out which factors need more or less emphasis. For example, if your AI is creating too many unnecessary gaps, try to increase the weight of the gap count cost factor. Feel free to use Turbo mode to dramatically speed up the testing process.
To earn full credit, your AI must be able to place at least 100 shapes, averaged across the provided TEST sequences. This is an improvement factor of about 40% above the performance when setting all weights to 1. To achieve this, manually adjust the weights. Of all these possible combinations, about 20% will result in an acceptable level of performance, so even just guessing some weight combinations won’t take too long yield a suitable result.
Please note that you can’t (unless you’ve completed the optional challenge below) develop a JUnit test to verify the level of performance of your weight combinations. Instead, you must confirm this by manually, by actually running the game in all four TEST modes, and making note of the displayed number of placements made for each mode. Again, Turbo mode will save a great deal of time during this process.
NOTE: Please do NOT rely upon Web-CAT for this; if you need any help, please ask in Piazza!
Test Coverage
Creating enough JUnit tests to cover all of your code will generally be very straightforward, since there aren’t very many branches involved in the solution. As a result, complete coverage is not nearly enough to ensure that you have correctly implemented your code.
Therefore, you must generate at least 5 test cases, with reasonably complex boards, for each interface method. You can satisfy this by creating at least 5 distinct test boards which you will simply share among assertions involving all the cost methods. You may find it useful to create a separate set of 5 test boards to test your findBestPlacement() method.
The test boards are expected cover a reasonably wide range of board layouts. You’re also encouraged to generate several simple test boards, to exercise cases like an empty board or an almost empty board. These simple boards don’t count toward your required 5 test cases mentioned above. Note that Web-CAT won’t (and can’t) enforce this requirement, but the human grader will definitely be looking for this.
Functional Decomposition
Judicious use of helper methods can significantly reduce the overall amount of code you need to develop, and help eliminate redundancies. Your code will be inspected during the human review for redundancies, so be sure to develop helper methods as appropriate.
SOLUTION
To begin with, you’ll need to download and play the game to understand its mechanics and how it works. Then, you need to implement the interface provided to you by the assignment. The interface will define the methods your AI needs to implement to interact with the game. These methods will typically include: evaluateBoard(Board board, Shape shape)
: This method takes the current state of the board and the next shape that will appear and returns a score indicating the quality of the placement. You’ll need to develop a cost-based algorithm to determine the best placement for the shape based on the current state of the board. bestPlacement(Board board, Shape shape)
: This method takes the current state of the board and the next shape that will appear and returns the best placement for the shape based on your cost-based algorithm.
Place an order in 3 easy steps. Takes less than 5 mins.