(Updated August 15, 2020)
Table of Contents
JavaFx
We will be using JavaFX for this next phase. The main logic has been designed and tested using the text-based game play model. Now we will begin designing the GUI portion. Below is a a rough picture with pixel heights and widths defined.
Figure 1: Detailed sizing values of roughed out game design.
The rough layout is used to define this next piece of code which is focused on making sure we get our shapes and colors nailed down and that they appear in the correct places.
Do not let yourself be overwhelmed by the size of the program. Nearly half of the code is commented out and was used for initial testing. It remains both as an artifact of how some design decisions were made in addition to having some basic testing nearby if we need it again.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.control.Button;
public class CodeBreakerFX extends Application
{
Color colors[] = { Color.RED, Color.WHITE, Color.ORANGE, Color.YELLOW, Color.BLUE, Color.GREEN };
public Circle[] genGuessResult(int gnum, int res) {
/*
This method is actually more complex than the others due to the
block arrangement of the pegs, and the fact that we are placing
four pegs in the array. Also, the numeric value of the result
requires a little special processing.
Remember that the res is ## where the first digit is the number
of exact matches and the second is the number of correct colors
in the wrong place.
The gnum processing is similar to the genGuess method except
the stacking of pegs into a 2x2 grid requires a bit more finesse
in the offset handling.
*/
Circle c[] = new Circle[4];
Color cc;
for ( int x = 0 ; x < 4; x++ ) {
// If test to select the peg color.
if ( res > 9 ) {
cc = Color.RED;
res -= 10;
} else if ( res > 0 ) {
cc = Color.WHITE;
res--;
} else {
cc = Color.LIGHTGREY;
}
/*
Calculate the center of the peg and determine the radius.
The block is 40x40. With 4 pegs at 10 pixels each, the gaps
were calculated as such: 40 - 20 = 20 / 3 = ~7 + 5 = 12.
That is, 40 pixels - 20 for pegs = 20 / 3 gaps (left, mid,
right or top, mid, bottom) is ~7 pixels plus the circle
radius is 12.
The peg arrangement is: 0 1
2 3
*/
int cx = 240;
int cy = 700 - gnum * 60;
switch (x) {
case 0:
cx += 12;
cy += 12;
break;
case 1:
cx += 28;
cy += 12;
break;
case 2:
cx += 12;
cy += 28;
break;
case 3:
cx += 28;
cy += 28;
break;
}
c[x] = new Circle(cx, cy, 5, cc);
c[x].setStroke(Color.BLACK);
}
return c;
}
public Circle genGuess(int gnum, int pos, Color c) {
Circle cir;
int x, y;
/*
Calculate the center of the peg. The first peg is 54
pixel from the left edge (20 outside box plus 24 for
the number of pixels between pegs plus 10 which is
the radius.
Subsequent pegs centers are 44 pixels away (24 gap plus
10 for new peg plus 10 for the previous peg.)
*/
x = 54 + pos * 44;
y = 700 - gnum * 60 + 20;
cir = new Circle(x, y, 10, c);
cir.setStroke(Color.BLACK);
return cir;
}
public Rectangle genRectangle(int x, int y, int w, int h) {
Rectangle r = new Rectangle(x, y, w, h);
r.setFill(Color.TRANSPARENT);
r.setStroke(Color.BLACK);
r.setArcWidth(20);
r.setArcHeight(20);
return r;
}
@Override
public void start(Stage primaryStage)
{
Group group;
Scene scene;
Button btn = new Button("Test me!");
group = new Group(btn);
//group = new Group();
scene = new Scene(group, 300, 800, Color.LIGHTGRAY);
/*
Each rectangle is 40 pixels tall. Guesses are 200 pixels wide
and report is 40 pixels wide. There are 20 pixels between each
set of boxes and thus the multiplier is 60 pixles.
*/
for ( int x = 0; x < 10; x++ ) {
int ypos = 700 - x * 60;
group.getChildren().add(genRectangle(20,ypos,200,40));
group.getChildren().add(genRectangle(240,ypos,40,40));
}
/*
Circle c = new Circle(60, 720, 10, Color.RED);
group.getChildren().add(c);
c = new Circle(100, 720, 10, Color.BLUE);
group.getChildren().add(c);
c = new Circle(140, 720, 10, Color.WHITE);
group.getChildren().add(c);
c = new Circle(180, 720, 10, Color.GREEN);
group.getChildren().add(c);
*/
// After the field of boxes is created, display the board.
primaryStage.setTitle("CodeBreaker");
primaryStage.setScene(scene);
primaryStage.show();
/*
// Manual test of first two guesses.
group.getChildren().add(genGuess(0, 0, Color.RED));
group.getChildren().add(genGuess(0, 1, Color.BLUE));
group.getChildren().add(genGuess(0, 2, Color.WHITE));
group.getChildren().add(genGuess(0, 3, Color.GREEN));
group.getChildren().add(genGuess(1, 0, Color.YELLOW));
group.getChildren().add(genGuess(1, 1, Color.BLUE));
group.getChildren().add(genGuess(1, 2, Color.ORANGE));
group.getChildren().add(genGuess(1, 3, Color.GREEN));
*/
btn.setOnAction( event -> {
for ( int y = 0; y < 4; y++ ) {
//try { Thread.sleep(500); } catch (Exception e) {}
group.getChildren().add(genGuess(0, y, colors[(int)(Math.random() * 6)]));
}
int exact = (int)(Math.random() * 5);
int correct = (int)(Math.random() * (5-exact));
int res = exact*10+correct;
System.out.println(res);
group.getChildren().addAll(genGuessResult(0, res));
/*
Circle c = new Circle(252, 712, 5, Color.RED);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
c = new Circle(268, 712, 5, Color.RED);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
c = new Circle(252, 728, 5, Color.WHITE);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
c = new Circle(268, 728, 5, Color.TRANSPARENT);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
/* debug */
});
/* button debug */
/*
// test filling all guesses with colors.
for ( int x = 0; x < 10; x++ ) {
for ( int y = 0; y < 4; y++ ) {
try { Thread.sleep(500); } catch (Exception e) {}
group.getChildren().add(genGuess(x, y, colors[(int)(Math.random() * 6)]));
}
}
// test filling guess report
for ( int x = 0; x < 10; x++ ) {
int exact = (int)(Math.random() * 5);
int correct = (int)(Math.random() * (5-exact));
System.out.println(exact*10+correct);
//group.getChildren().add(genGuess(x, y, colors[(int)(Math.random() * 6)]));
}
/* debug */
/*
// test putting color in the results area.
Circle c = new Circle(252, 712, 5, Color.RED);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
c = new Circle(268, 712, 5, Color.RED);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
c = new Circle(252, 728, 5, Color.WHITE);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
c = new Circle(268, 728, 5, Color.TRANSPARENT);
c.setStroke(Color.BLACK);
group.getChildren().add(c);
/* debug */
}
public static void main(String[] args) {
// standard JavaFX Application object launch
launch(args);
}
}
Iteration 1: Design concept for JavaFX version of game.
Refer back to the JavaFX in Chapter 7b for any details on the implementation and shapes used.
The code first defines a basic layout and presents the user with a game board similar to the rough layout. This is shown in Figure 2.
Figure 2: Start of rough game test.
Figure 3: User clicks the Test me! button to load colored pegs.