You will write a multi-interface version of the well-known concentration game: 1. The game
displays a grid of upper-case letters, with each letter appearing twice. 2. A player has a few
seconds to memorize the letters before they disappear. 3. The player then has to remember where
each pair was located.
line, then MultiConcentration starts with the text interface.
First the new game display will show the user the pairs he/she must guess, in a format similar to
the following example for size = 6
D H B C M I
H G K K A R
C N R E O E
Q O A Q L F
L F J P B G
P D N M I J
Memorize the above grid!
Note that the new game display uses pairs of distinct single uppercase capital letters distributed
at random on a square grid, starting at A and continuing until the grid is full.
This new game display shows for 10 seconds, after which it scrolls out of view. (To scroll it just
write about 25 newlines.) Then the standard game display appears.
The standard game display will look like the following example for size = 6
1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18
19 20 21 22 23 24
25 26 27 28 29 30
21 32 33 34 35 36
Enter a pair of numbers, or \"R\" to reset, or \"Q\" to quit:
reset, or \"Q\" to quit:
If the player makes an invalid entry (e.g. numbers out of range, number already guessed, no
blank separator, etc.) then a \"please reenter\" message is printed and the same display is shown
again.
If the player makes a bad guess, then a \"Sorry...\" message is printed and the same display is
shown again.
If the player enters an \"R\" for reset, then we start over, that is, the computer calculates a new
set of pairs and shows the new game display again.
If the player enters a \"Q\" for quit, then the game prints a \"Game Over\" message and ends.
3.4 Graphic Game Interface
If the player used the \"-g\" flag on the startup command line then MultiConcentration starts up
with the graphic interface.
You may design the graphic interface as you choose, as long as you use Swing and preserve the
steps in the game as described in the previous section.
One possible graphic interface is shown in Figure 1. In this design the new game display and the
standard game display have been replaced by a grid of buttons. Instead of entering pairs of
numbers, the player clicks on two of the buttons. The \"reset\" and \"quit\" commands are given
using a menu. Letters that have been correctly guessed are shown with a pink background color.
Messages to the player are shown in a text area under the grid.
4 GENERAL REQUIREMENTS
4.1 Design Requirements
Design your program with GUI classes, a main class, and Application Logic / Data classes as
described in my overheads on Design for Testability.
Do not use a package statement; name the main class MultiConcentration. (Otherwise the
startup command given in 3.1 would not work.)
You should have at least 5 classes, and not one of them should have more than 40% of the code.
Solution
import java.io.OutputStream;
import java.io.PrintStream;
public cl.
You will write a multi-interface version of the well-known concentra.pdf
1. You will write a multi-interface version of the well-known concentration game: 1. The game
displays a grid of upper-case letters, with each letter appearing twice. 2. A player has a few
seconds to memorize the letters before they disappear. 3. The player then has to remember where
each pair was located.
line, then MultiConcentration starts with the text interface.
First the new game display will show the user the pairs he/she must guess, in a format similar to
the following example for size = 6
D H B C M I
H G K K A R
C N R E O E
Q O A Q L F
L F J P B G
P D N M I J
Memorize the above grid!
Note that the new game display uses pairs of distinct single uppercase capital letters distributed
at random on a square grid, starting at A and continuing until the grid is full.
This new game display shows for 10 seconds, after which it scrolls out of view. (To scroll it just
write about 25 newlines.) Then the standard game display appears.
The standard game display will look like the following example for size = 6
1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18
19 20 21 22 23 24
25 26 27 28 29 30
21 32 33 34 35 36
Enter a pair of numbers, or "R" to reset, or "Q" to quit:
reset, or "Q" to quit:
If the player makes an invalid entry (e.g. numbers out of range, number already guessed, no
blank separator, etc.) then a "please reenter" message is printed and the same display is shown
again.
If the player makes a bad guess, then a "Sorry..." message is printed and the same display is
shown again.
If the player enters an "R" for reset, then we start over, that is, the computer calculates a new
set of pairs and shows the new game display again.
If the player enters a "Q" for quit, then the game prints a "Game Over" message and ends.
2. 3.4 Graphic Game Interface
If the player used the "-g" flag on the startup command line then MultiConcentration starts up
with the graphic interface.
You may design the graphic interface as you choose, as long as you use Swing and preserve the
steps in the game as described in the previous section.
One possible graphic interface is shown in Figure 1. In this design the new game display and the
standard game display have been replaced by a grid of buttons. Instead of entering pairs of
numbers, the player clicks on two of the buttons. The "reset" and "quit" commands are given
using a menu. Letters that have been correctly guessed are shown with a pink background color.
Messages to the player are shown in a text area under the grid.
4 GENERAL REQUIREMENTS
4.1 Design Requirements
Design your program with GUI classes, a main class, and Application Logic / Data classes as
described in my overheads on Design for Testability.
Do not use a package statement; name the main class MultiConcentration. (Otherwise the
startup command given in 3.1 would not work.)
You should have at least 5 classes, and not one of them should have more than 40% of the code.
Solution
import java.io.OutputStream;
import java.io.PrintStream;
public class MultiConcentration {
public static boolean gameTestModeEnabled = false;
private static boolean textBasedGame = false;
private static int gridSize = -1;
public static final int LOW_GRID = 2;
public static final int HI_GRID = 7;
private static void usage() {
System.out
.println("Usage: java [-ea] MultiConcentration [-g|-t] gridSize");
System.out.println("-ea for enabling assertions");
System.out.println("-g for a GUI interface");
System.out.println("-t for a text interface");
System.out
3. .printf("gridSize is the number of rows or columns (must be %d to %d) ",
LOW_GRID, HI_GRID);
}
private static boolean validateArguments(String[] args) {
// check for test mode
if (args.length == 1) {
if (args[0].equals("-TEST_MODE")) {
gameTestModeEnabled = true;
return true; // All good
}
}
// check for input arguments
if (args.length < 2) {
System.err.println("Error: Not enough arguments given.");
// not enough args given
return false;
} else {
if (!(args[0].equals("-t") || args[0].equals("-g"))) {
System.err.println("Error: First argument is invalid.");
// mistake in first argument
return false;
}
// set textBasedGame
if (args[0].equals("-t"))
textBasedGame = true;
else
textBasedGame = false;
// check second argument
try {
gridSize = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
System.err.println("Error: Second argument is not a number.");
return false; // not integer
}
// Test grid size
4. if (!(gridSize >= LOW_GRID && gridSize <= HI_GRID)) {
System.err.println("Error: Second argument is not in "
+ LOW_GRID + " <= x <= " + HI_GRID + ".");
return false;
}
return true; // All good
}
}
private static void logMsg(boolean passed, int testCnt) {
if (!passed)
System.out.printf("MultiConcentration.validateArguments MC_"
+ "%03d failed ", testCnt);
else
System.out.printf("MultiConcentration.validateArguments MC_"
+ "%03d passed ", testCnt);
}
private static boolean classTest() {
boolean allPassed = true;
int tstCnt = 1;
String[] testArgs = { "" };
boolean result = validateArguments(testArgs);
if (result)
allPassed = false;
logMsg(allPassed, tstCnt++);
allPassed &= true;
// Test Case: MC_002 - Test for missing flag character or incorrect flag
// character.
String[] testArgs2 = { "-a", "" };
result = validateArguments(testArgs2);
if (result)
allPassed = false;
logMsg(allPassed, tstCnt++);
allPassed &= true;
// Test Case: MC_003 - Tests gridSize argument that is less than
// minimum.
testArgs2[0] = "-t";
5. testArgs2[1] = "1";
result = validateArguments(testArgs2);
if (result)
allPassed = false;
logMsg(allPassed, tstCnt++);
allPassed &= true;
// Test Case: MC_004 - Tests gridSize argument within range, between 2
// and 7 with text GUI.
testArgs2[0] = "-t";
testArgs2[1] = "4";
result = validateArguments(testArgs2);
if (!result)
allPassed = false;
logMsg(allPassed, tstCnt++);
allPassed &= true;
// Test Case: MC_005 - Tests gridSize argument within range, between 2
// and 7 with graphic GUI.
testArgs2[0] = "-g";
testArgs2[1] = "4";
result = validateArguments(testArgs2);
if (!result)
allPassed = false;
logMsg(allPassed, tstCnt++);
allPassed &= true;
// Test Case: MC_006 - Tests gridSize argument that is greater than
// maximum.
testArgs2[0] = "-t";
testArgs2[1] = "8";
result = validateArguments(testArgs2);
if (result)
allPassed = false;
logMsg(allPassed, tstCnt++);
allPassed &= true;
// Test Case: MC_007 - Test for extra flag characters.
testArgs2[0] = "-tag";
result = validateArguments(testArgs2);
7. overAllTestResult &= MultiConcentration.systemTests();
if (overAllTestResult)
System.out.println("All tests passed");
else
System.out.println("Some tests failed");
}
public static void main(String[] args) {
// check arguments
if (!validateArguments(args)) {
usage();
System.exit(1);
}
// Test Mode?
if (gameTestModeEnabled) {
// Error if unit tests are run without assertions enabled
boolean assertsEnabled = false;
assert assertsEnabled = true; // intentional side effect
if (!assertsEnabled)
throw new RuntimeException(
"Assertions must be enabled for unit tests");
// disable error output
System.setErr(new PrintStream(new OutputStream() {
public void write(int b) {
}
}));
startClassTests();
} else {
// init and start game
GameController game = new GameController(gridSize, textBasedGame);
game.startGame();
}
}
}
GameConfiguration.java
8. import java.util.Arrays;
public class GameConfiguration {
private char gameMatrix[];
/** currentMatches represents the found current matches of the user */
private boolean currentMatches[];
/** gridSize is the size of the game */
private int gridSize;
public GameConfiguration(int gridSize, char gameMatrix[])
throws NullPointerException, IllegalArgumentException {
if (gameMatrix == null)
throw new NullPointerException("gameMatrix is null");
if (!(gridSize >= MultiConcentration.LOW_GRID && gridSize <=
MultiConcentration.HI_GRID))
throw new IllegalArgumentException("Game size out of range");
if (gameMatrix.length != gridSize * gridSize)
throw new IllegalArgumentException("Game matrix dimension error");
this.gridSize = gridSize;
this.gameMatrix = gameMatrix;
currentMatches = new boolean[gameMatrix.length];
for (int i = 0; i < currentMatches.length; i++) {
currentMatches[i] = false;
}
}
public boolean isValidFieldNumber(int number) {
// out of range?
if (number < 1 || number > gridSize * gridSize) {
return false;
}
// already chosen?
if (currentMatches[number - 1])
return false;
return true;
}
public boolean trySetMatch(int matchPosition1, int matchPosition2)
throws IllegalArgumentException {
if (matchPosition1 <= 0 || matchPosition1 > gameMatrix.length)
9. throw new IllegalArgumentException("matchpos 1 out of range");
else if (matchPosition2 <= 0 || matchPosition2 > gameMatrix.length)
throw new IllegalArgumentException("matchpos 1 out of range");
if (gameMatrix[matchPosition1 - 1] == gameMatrix[matchPosition2 - 1]) {
currentMatches[matchPosition1 - 1] = true;
currentMatches[matchPosition2 - 1] = true;
return true;
} else {
return false;
}
}
public boolean isGameFinished() {
boolean retVal = false;
// Base number of correct answers on length
int numCorrectNeeded = currentMatches.length;
int numCorrectCnt = 0;
// If grid is an odd number of elements, all but one
// correct count indicates game over
if (0 == ((numCorrectNeeded + 1) % 2)) {
numCorrectNeeded--;
}
// count number of matches
for (int i = 0; i < currentMatches.length; i++) {
if (currentMatches[i])
numCorrectCnt++;
}
// Determine if enough matches are true
if (numCorrectCnt == numCorrectNeeded)
retVal = true;
return retVal;
}
/**
* @return the gameMatrix
*/
public char[] getGameMatrix() {
return gameMatrix;
11. // GML2 = {gameMatrix: gameMatrix.length() == gridSize^2}
char GMC1[] = { 'a', 'A', 'B', 'B' }; // {gameMatrix: gameMatrix[X] is
// no capital char}
char GMC2[] = GMN2; // {gameMatrix: gameMatrix[X] is a capital char}
char GMC[][] = { GMC1, GMC2 };
char GMM1[] = GMN2; // {gameMatrix: For every X, there is only one or no
// Y for: gameMatrix[X] = gameMatrix[Y]}
char GMM2[] = { 'A', 'A', 'A', 'B' }; // {gameMatrix: There is a X with
// more than one Y for:
// gameMatrix[X] =
// gameMatrix[Y]}
char GMMC[][] = { GMM1, GMM2 };
/** Test case 1 */
passed = true;
try {
config = new GameConfiguration(GS1, GMN2);
passed = false;
} catch (IllegalArgumentException e) {
}
try {
config = new GameConfiguration(GS2 + 1, GMN2);
passed = false;
} catch (IllegalArgumentException e) {
}
// print message and prepare for next test
logMsg(passed, "GameConfiguration", testCount, 'C');
overallPassed &= passed;
testCount++;
/** Test case 2 */
passed = true;
try {
config = new GameConfiguration(GS2, GMN1);
passed = false;
} catch (NullPointerException e) {
}
// print message and prepare for next test
12. logMsg(passed, "GameConfiguration", testCount, 'C');
overallPassed &= passed;
testCount++;
/** Test case 3 */
passed = true;
try {
config = new GameConfiguration(GS3, GMN2);
passed = false;
} catch (IllegalArgumentException e) {
}
// print message and prepare for next test
logMsg(passed, "GameConfiguration", testCount, 'C');
overallPassed &= passed;
testCount++;
/** Test case 4 */
passed = true;
try {
config = new GameConfiguration(GS2, GMC1);
passed = false;
} catch (IllegalArgumentException e) {
}
// print message and prepare for next test
logMsg(passed, "GameConfiguration", testCount, 'C');
overallPassed &= passed;
testCount++;
/** Test case 5 */
passed = true;
try {
config = new GameConfiguration(GS2, GMM2);
passed = false;
} catch (IllegalArgumentException e) {
}
// print message and prepare for next test
logMsg(passed, "GameConfiguration", testCount, 'C');
overallPassed &= passed;
testCount++;
13. /** Test case 6 */
passed = true;
config = new GameConfiguration(GS2, GMM1);
// check if object is initialized right
if (config.getCurrentMatches().length != GS2 * GS2)
passed = false;
else {
for (boolean currMatch : config.getCurrentMatches()) {
if (currMatch) {
passed = false;
break;
}
}
}
// print message and prepare for next test
logMsg(passed, "GameConfiguration", testCount, 'C');
overallPassed &= passed;
testCount++;
return overallPassed;
}
private static boolean isValidFieldNumberTest() {
int testCount = 1;
boolean passed = true;
boolean overallPassed = true;
int N1 = -1; // {number: number < 1}
int N2 = 2; // {number: 1 <= number <= gridSize * gridSize}
int N3 = 1000; // {number: number > gridSize * gridSize}
// CM1 = {currentMatches: currentMatches[number] == true}
// CM2 = {currentMatches: currentMatches[number] == false}
/** init test data */
char matrix[] = { 'A', 'A', 'B', 'B' };
GameConfiguration config = new GameConfiguration(2, matrix);
config.trySetMatch(1, 2);
/** Test case 1 */
passed = true;
if (config.isValidFieldNumber(N1))
14. passed = false;
if (config.isValidFieldNumber(N3))
passed = false;
// print message and prepare for next test
logMsg(passed, "isValidFieldNumber", testCount, 'F');
overallPassed &= passed;
testCount++;
/** Test case 2 */
passed = true;
if (config.isValidFieldNumber(N2 + 1) == false)
passed = false;
// print message and prepare for next test
logMsg(passed, "isValidFieldNumber", testCount, 'F');
overallPassed &= passed;
testCount++;
/** Test case 3 */
passed = true;
if (config.isValidFieldNumber(N2) == true)
passed = false;
// print message and prepare for next test
logMsg(passed, "isValidFieldNumber", testCount, 'F');
overallPassed &= passed;
testCount++;
return overallPassed;
}
private static boolean trySetMatchTest() {
int testCount = 1;
boolean passed = true;
boolean overallPassed = true;
int M1_1 = -1; // {matchPosition1: matchPosition1 < 1}
int M1_2 = 1; // {matchPosition1: 1 <= matchPosition1 <= gridSize *
// gridSize}
int M1_3 = 5; // {matchPosition1: matchPosition1 > gridSize * gridSize}
int M2_1 = -1; // {matchPosition2: matchPosition2 < 1}
int M2_2 = 2; // {matchPosition2: 1 <= matchPosition2 <= gridSize *
// gridSize}