Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

The purpose of this assignment is to implement the Eliminate Game logic in a component called the EliminationGameEngine. Game Description You always start with a

The purpose of this assignment is to implement the Eliminate Game logic in a component called the EliminationGameEngine.

Game Description

You always start with a score of 78 which is the sum of the numbers from one to twelve. As an example, on your first roll of the dice you roll a five and a two. You may "eliminate" the number five and the number two or you may eliminate the number seven. Either way your score decreases by seven. You must eliminate the sum of the dice if you roll doubles. That is, if you roll a three and a three, you must eliminate six.

You cannot eliminate the same number more than once per game. The game ends when you roll the dice and cannot eliminate all the numbers. Your final game score is the sum of all the numbers you have not eliminated.

Week12 will focus on implementing the engine. There is a test harness provided and the classes are specified, i.e. defined at the public interface level. You MUST conform to that design. In Week13, we will create a graphical interface to access the game engine.

We will use a layered architecture to separate out the presentation and business layers. This supports two key characteristics of a design: high cohesion and low coupling. It allows us to test the game logic (business rules) independent of the presentation.

image text in transcribed

See the JUnit tests attached below. You MUST design the classes using the following interface.

You need to configure your Eclipse to run JUnit.

Use week12 as your package

image text in transcribed

The JUnit test explicitly use the interface defined in the UML description below:

Please note that the week01 package reference isn't pertinent to this discussion, we are using week12.

image text in transcribed

Week12 Class Model

You MUST use the public method interfaces defined above The class diagram above does not include the user interface. Your GUI must use the EliminationGameEngine code. The JUnit tests actually play the game like the user would. The tests verify the game logic is correct. The presentation (GUI) layer uses the engine code. This is code reuse and a separation of concerns.

Class Descriptions:

This section outlines how the classes are used.

image text in transcribed

You are creating and implementing the three classes defined above. The UML shows the public and private methods and attributes. When developing to a provided design, you are responsible for strict conformance to the public methods and behaviors. This often called "design-by-contract" and it allows consumers of the class to accurately develop their code concurrently and allow for a seamless integration later because both sides of the interface were honored.

To get us started, we will start with the two helper classes, DiceSelection and GameEntry. The design principles that led to these two classes are cohesion and coupling. Cohesion means grouping behavior together, do one thing only. By separating out the rolling of the die, it simplifies the EliminationGameEngine because it only needs to be concerned about the values of the die, not how they are generated. Coupling is how tightly integrated two things are. In this design, the "how" of rolling die is isolated from the EliminationGameEngine, so it is loosely coupled to the die rolling.

Detailed Description of DiceSelection

image text in transcribed

This class completely implements the behavior for rolling two dice six-sided die. It utilizes the java.util.Random (Advanced Java Concept). The public methods above, denoted by the + symbol are the constructor DiceSelection, methods roll, getDie1, getDie2, and reset. The private method dieRoll() is what I used to actually roll a single die. It is the only method that interacts with the Random class instance.

Behavior: The class must retain the values of the last dice rolled. Values must be between 1-6 inclusive. When you create the class or reset, the values are zero (0). The user must roll once to get a valid value.

What is expected at this point is that you:

Create the class shell with the public methods

Use Javadoc to research the java.util.Random class. All programming languages have a random library.

Test against the DiceSelectionTest JUnit test

Detailed description of GameEntry

image text in transcribed

In the elimination game, the two die can generate the following range of numbers: 2,3,4,5,6,7,8,9,10,11,12

Each number can be selected during the game based on the sum of the die or the individual die value from a roll. So in this design, we use the class GameEntry to encapsulate the behavior and state of these individual numbers. When the user wants to select a number, there is some significant logic to determine if the number is allowed to be selected, but once that is done, the number, our GameEntry, manages the state of that instance of the number.

The GameEntry class maintains the following stateful attributes: the value and whether it is selected or not.

It provides the following public methods: constructor GameEntry that takes a number as a value, getters to return the value as an integer or a String, a Boolean method on whether the entry has been selected, a method to select the entry and a setter to set the value.

What is expected at this point is that you:

Create the class shell with the public methods

Implement the methods and manage the internal state. The private member attributes in the UML are how I did it.

Test against the GameEntryTest JUnit test

EliminationGameEngine

Now that we have some helper classes, we focus on the game engine. The EliminationGameEngine class implements the rules of the game. It is used by the test harness and the graphical user interface we will build in the next assignment. It contains state and behavior.

image text in transcribed

The EliminationGameEngine is composed of a DiceSelection instance and an array of GameEntry's corresponding to the set of allowable numbers when there is a roll. It implements the logic of the game such as a double roll must select the sum and only the values of the dice can be selected individually.

There is one internal method, veriflySelectedEntry, and verifies that selection must be one of the rolled die values or the sum. This is fed back to the calling public method.

Now, this is where it gets exciting. While the interface methods are defined, you have to implement the game logic.

I used the following state model to support the reference implementation.

image text in transcribed

So how does this work.

When the user (or test) creates an instance of the EliminationGameEngine, the constructor creates the DiceSelection instance and generates the GameEntry arrays for the allowable values. It also sets the internal state of the game to NOT_READY. When the user (or test) invokes resetGame(), the game re-initializes the GameEntry classes back to defaults, resets the internal attributes like the rollDie attrabutes and sets the game state to READY.

Now the user (or test) can invoke roll. Prior to that transition, any invocation of roll() would be ignored. If the roll generates a set of die that cannot be played, the game is lost (game over) and the engine transitions to LOST. If the user resets the game, it transitions to READY and a new set of rolls is allowed.

Now, on a successful roll, this is where it gets tricky; if the user selects the number that is the sum of the two die, then the corresponding GameEntry is marked selected and the engine transitions back to READY. You can only roll in the READY state. If the user selects only one of the die, the the corresponding GameEntry is marked selected and the engine transitions to ROLLED2, waiting for the user to make a second selection. If that selection is valid, the corresponding GameEntry is marked selected and the engine transitions to READY for the next roll. If the selection is invalid, the engine transitions to LOST, game over.

There is a key point to make on the selectEntry method.

/** * Tells the game which values to select (disable) * * if the provided value equals the sum of the rolled die, then the user * selected a die representing the sum of both, otherwise the user selected * a single die. * * If the entry hasn't been selected yet, process the next state. If it has, * then the state is LOST * * @param value * The entry value to disable * @return GAME_STATE based on the business logic transition * @throws InvalidSelectionException if the value isn't a rolled die value or their sum */

The exception class inherits from the Exception class

image text in transcribed

If you don't throw the exception the EliminationGameEngineTest will generate a compile error for unreachable code.

What is expected at this point is that you:

Create the class shell with the public methods

Implement the methods and manage the internal state. The private member attributes in the UML are how I did it.

Implement the state machine behavior

Test against the EliminationGameEngineTest JUnit test

Turn In: DiceSelection.java EliminationGameEngine.java GameEntry.java

Files provided:

InvalidSelectionException.java

package week12; public class InvalidSelectionException extends Exception { /** * */ private static final long serialVersionUID = -3914208489902564726L; /** * Custom constructor * @param msg Text message */ public InvalidSelectionException(String msg) { super(msg); } }

DiceSelectionTest.java package week12; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class DiceSelectionTest { DiceSelection diceClass; static int TEST_RUNS = 1000; static int[] bins = {0,0,0,0,0,0}; @Before public void initialize() { diceClass = new DiceSelection(); } @Test public void testRoll() { int die1Before; int die2Before; int die1Actual; int die2Actual; boolean unMatched = false; for(int i = 0; i

EliminationGameEngineTest.java package week12; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import week12.EliminationGameEngine.GAME_STATE; import java.util.Random; /** * This test plays the game programmatically. It creates an instance of the game * engine and interacts with the game. It uses the GAME_STATE result to * determine if should keep rolling or game is over. * * @author Scott * */ public class EliminationGameEngineTest { EliminationGameEngine engine; Random rand; int expectedMaxScore = 78; @Before public void setUp() throws Exception { engine = new EliminationGameEngine(); rand = new Random(); } @Test public void testResetEliminationGameEngine() { trace(" -- testResetEliminationGameEngine --"); engine.resetGame(); int score = engine.getScore(); Assert.assertEquals(expectedMaxScore, score); } @Test public void testInvalidSelectionException() { trace(" -- testInvalidSelectionException --"); try { //GAME_STATE result = GAME_STATE.LOST; engine.resetGame(); engine.roll(); int die1 = engine.getDieValues()[0]; trace(" Rolled die value: " + die1); int testValue = die1 % 6 + 1; // generate an invalid selection trace(" Test value: " + testValue); engine.selectEntry(testValue); Assert.fail("Expected InvalidSelectionException"); } catch(InvalidSelectionException ex) { // successful test trace(" InvalidSelectionException: "); } } @Test public void testEliminationGameEngine() { trace(" -- testEliminationGameEngine --"); engine.resetGame(); boolean test1 = testEntryArray(); Assert.assertTrue("Test Entry Array Init", test1); try { boolean test2 = testPlayGame(); Assert.assertTrue("Game results", test2); } catch(Exception ex) { Assert.fail(ex.getMessage()); } } /** * This plays the game. It rolls until there are no more * * @return true if successful * @throws InvalidSelectionException if a selection is not * a die value or die sum value */ private boolean testPlayGame() throws InvalidSelectionException { boolean result = true; int score = engine.getScore(); Assert.assertEquals(expectedMaxScore, score); int passes = 0; engine.roll(); result = evaluate(engine.getDieValues()[0], engine.getDieValues()[1]); Assert.assertTrue("Unexpected termination", result); passes++; while(result) { engine.roll(); trace("Roll game state: " + engine.getGameState()); passes++; result = evaluate(engine.getDieValues()[0], engine.getDieValues()[1]); } trace(String.format("Test over. Passes: %d Score %d", passes, engine.getScore())); return true; } /** * Evaluates a roll of die. The algorithm will first evaluate if the die are * equal which requires adding them. The next thing it will do is see if it * can add the values and select, if not it will try one or the other value * (random selection). * * @param one * die1 value * @param two * die2 value * @return true if the value was selected successfully. * @throws InvalidSelectionException if a selection is not * a die value or die sum value */ private boolean evaluate(int one, int two) throws InvalidSelectionException { GAME_STATE result = GAME_STATE.LOST; boolean readyState = false; trace(String.format("die1 %d, die2 %d", one, two)); // if they are the same, you must add and select if(one == two) { trace(String.format(" selecting %d", one + two)); int sum = one + two; if(!engine.isSelected(sum)) { result = engine.selectEntry(one + two); } } else { // try adding them and select int useValue = one + two; if(!engine.isSelected(useValue)) { trace(String.format(" selecting %d - sum", useValue)); result = engine.selectEntry(useValue); } else // sum was already selected { // get a random option for one of the two values int select = rand.nextInt(2); useValue = select == 0 ? one : two; if(!engine.isSelected(useValue)) { trace(String.format(" selecting %d - individual", useValue)); result = engine.selectEntry(useValue); } switch(result) { case ROLLED2: // process the second number useValue = useValue == one ? two : one; trace(String.format(" selecting %d - individual", useValue)); result = engine.selectEntry(useValue); break; case ROLLED: break; case READY: break; case NOT_READY: break; case LOST: break; } } // trace(String.format(" selecting %d", useValue)); } trace(String.format(" score %d, current state %s", engine.getScore(), result)); // should the game continue? Only roll in the READY state readyState = result == GAME_STATE.READY; return readyState; } private boolean testEntryArray() { boolean result = true; GameEntry[] entries = engine.getEntries(); int expected = 1; for(GameEntry entry : entries) { Assert.assertEquals(entry.getValue(), expected++); } return result; } private void trace(String msg) { System.out.println(msg); } }

TestHarness.java package week12; import java.util.List; import org.junit.runner.Result; import org.junit.runner.notification.Failure; public class TestHarness { public static void main(String[] args) { new TestHarness().runTests(); } private void runTests() { try { boolean diceTest = testDieSelection(); boolean gameEntryTest = testGameEntry(); boolean gameTest = testGame(); boolean result = diceTest && gameEntryTest && gameTest; trace(result ? "Tests Passed" : "Tests Failed"); } catch(Exception ex) { trace(ex.getMessage()); } } private boolean testDieSelection() { boolean success = true; Result result = org.junit.runner.JUnitCore .runClasses(DiceSelectionTest.class); int failCount = result.getFailureCount(); if(failCount > 0) { List failures = result.getFailures(); for(Failure fail : failures) { trace("FAILED: " + fail.getMessage()); success = false; } } return success; } private boolean testGameEntry() { boolean success = true; Result result = org.junit.runner.JUnitCore .runClasses(GameEntryTest.class); int failCount = result.getFailureCount(); if(failCount > 0) { List failures = result.getFailures(); for(Failure fail : failures) { trace("FAILED: " + fail.getMessage()); success = false; } } return success; } private boolean testGame() { boolean success = true; Result result = org.junit.runner.JUnitCore .runClasses(EliminationGameEngineTest.class); int failCount = result.getFailureCount(); if(failCount > 0) { List failures = result.getFailures(); for(Failure fail : failures) { trace("FAILED: " + fail.getMessage()); success = false; } } return success; } static private void trace(String msg) { System.out.println(msg); } }

GameEntryTest.java package week12; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * Tests the GameEntry class * * @author Scott * */ public class GameEntryTest { @Before public void initialize() { } @Test public void testGameEntryMethods() { int expectedValue = 1; int updatedValue = 2; String expectedString = "1"; String updatedString = "2"; GameEntry actual = new GameEntry(expectedValue); Assert.assertTrue(actual.getValue() == expectedValue); Assert.assertTrue(actual.getValueString().equals(expectedString)); Assert.assertFalse(actual.isSelected()); actual.setSelected(); Assert.assertTrue(actual.isSelected()); actual.clearSelected(); Assert.assertFalse(actual.isSelected()); actual.setValue(updatedValue); Assert.assertTrue(actual.getValue() == updatedValue); Assert.assertTrue(actual.getValueString().equals(updatedString)); } }

composite structure Architecture Presentation Layer block Utilities EliminationGameEngine Business Rules composite structure Architecture Presentation Layer block Utilities EliminationGameEngine Business Rules

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Repairing And Querying Databases Under Aggregate Constraints

Authors: Sergio Flesca ,Filippo Furfaro ,Francesco Parisi

2011th Edition

146141640X, 978-1461416401

More Books

Students also viewed these Databases questions