|
CS 112 - Introduction to Computer Science II
Homework #10 |
Due: At the beginning of class 30.
NOTE: This work is to be done in pairs. I strongly recommend the
practice of Pair
Programming described simply
here. Although team members are permitted to divide
work, each team member should be able to informally present all work of
his/her teammate.
Lights Out Puzzle Application
Overview of Game
In this exercise, you will implement a "Lights
Out" puzzle game graphical application, with selectable grid sizes, initial
and button-triggered randomization, and "light
chasing" button.
Lights Out is a puzzle where the player is presented with an n-by-n
grid of lights where some lights are on and some lights are off. The
object of the puzzle is to turn all lights off (i.e. "lights out"). We say
that a light is toggled when it changes state, that is, off to on or on to off.
When a light is selected (i.e. pressed), that light and all lights immediately
adjacent horizontally and vertically are toggled. This will be illustrated
in the screenshots below.
Not every configuration of on/off lights for all grid sizes is solvable, but
all of our puzzles will be created in such a way as to be solvable.
You can watch this
video demonstration of the application you will create.
Game Implementation Specifications
You will implement class LightsOut, extending Application. When
LightsOut is executed, the first and only stage shown, titled "Lights Out Size"
(not visible in window, but visible when minimized), requests the game size
from the user as shown in this image:
Note that the user is allowed to select sizes 3-9 from radio buttons.
The "Please select a size:" prompt label, the radio buttons labeled 3-9, and the
"Create Puzzle" button confirming the user's selection should be centered in
vertical alignment, and separated by at least 5 pixels between components (e.g.
new VBox(5)).
The radio button "5" should be selected by default:
Let us suppose the user selects radio button "3" to create a 3-by-3 puzzle.
Correctly grouped radio buttons will cause radio button "5" to then be
deselected:
When the user then clicks the "Create Puzzle" button, a new puzzle stage is
created and then the initial size-selection stage is closed:
This new puzzle stage is initialized as follows:
- This non-resizable puzzle stage has title "Lights Out", has a minimum
width of 250, a width of (60 * size), and a height of (60 * size) + 60, where size
is n for the n-by-n grid. (Note: This change in specification isn't reflected in the figures below.)
- A BorderPane is created with an HBox at the bottom and a GridPane in the
center.
- HBox
- center-aligned with spacing of 20
-
preferred height of 60 (Note: This change in specification isn't reflected in the figures below.)
- contains two buttons:
- "Randomize": With 0.5 probability, toggles each light
button.
- "Chase Lights": For each row from the top to the
second-to-bottom, iterate through columns. For each light
that is on, cause the same effect as if the the light below it
in the grid had been pressed.
- GridPane
- The pane is center-aligned with a fully opaque background color
with red, green, and blue having hexadecimal value 55.
- A new grid of Buttons is created that is n-by-n,
where n is the size selected by the user.
- Each 50-by-50-pixel button is made on/off by setting the button
background to yellow/black, respectively, with corner radii of
10, and insets of 1. Initially, each button is initialized as
off.
- When a button is pressed, it and any horizontally or vertically
adjacent buttons are toggled.
- After all buttons are created, they are randomized, i.e. with
0.5 probability, a light button is pressed, toggling itself and orthogonally adjacent light buttons. (This ensures that there
is a solution, e.g. the same pressing would return all buttons to
the off state.)
After randomly pressing or not pressing light buttons in a lights out grid, this is one example initial
3-by-3 puzzle grid stage:
One strategy is called "light chasing": Pick an uppermost light that is on
and press/select the light immediately beneath it. Continue this process
until the only lights on are in the bottom row. As an example, we will
successively click the light below the uppermost, leftmost light that is on.
In this case, we first click below the upper-middle light, i.e. we click the
center light with the following result:>
Note that not only the selected/pressed middle light has been toggled, but
also the four lights adjacent above, below, leftward, and rightward have been
toggled as well. If we continue to manually "chase lights", we
pass through the following states until the only lights on (if any) are in the bottom row:
Suppose that I click the upper-right button in this state. Note how adjacency
toggling does not wrap around, e.g. from right side to
left side, or top to bottom:
If I click the "Chase Lights" button, the same process will take place, leading (in this case) to a solved
grid with all lights out:
If I click the "Randomize" button, then the same randomization process
performed initially is performed on the current state (solved or not). In
this instance, it creates a new puzzle for solving:
The application is terminated by clicking the red "X" button in the
upper-right of the stage window.
Specific Implementation Recommendations
There are particular private fields and methods that are not required, yet
may be helpful to your design.
- Keeping the grid size in a private field is handy.
- You will benefit from keeping your Buttons not only in their GridPane,
but also in a 2D array private field for easy access.
- Since
setting the button Background is syntactically arduous, you'll likely
benefit from creating two Background constants for on (yellow) and off
(black) that you can use for setting and checking button background values.
- These are some private methods you may find useful to implement:
- private void randomize()
- private void chaseLights()
- private void press(int row, int col) - Note: This is what allows one
to easily simulate button presses when randomizing or chasing lights.
If the light button presses and "Randomize"/"Chase Light" button presses
rely on this method, there is no redundant code and all is as if virtual
light button presses are being made when programmatically randomizing or
chasing lights.
- private void toggleLight(int row, int col)
- private boolean isLightOn(int row, int col)
- (Each of these can be (but need not be) implemented in 4 lines or
less.)
- There will be times that you want to use a variable in a handler (e.g.
within the code block of a lambda expression) where the variable must be
final in order for the compiler to allow such usage. For example, you
may want to use your row/column loop control variables in a call to a press
method. The solution is awkward but simple: declare local final
variables before you create and set the handler, assign those local final
variables from your non-final variables, and then you can use your local
final variables within your handler.
My implementation is less than 150 lines of Java. If yours is
significantly more, feel free to stop by office hours to see how your code may
be streamlined.
Rubric: (20 points total)
- 3 points: Size Selection Stage:
- 1 point: Only the size selection stage with title "Lights Out
Size" is shown initially.
- 1 point: The size selection stage has all listed items centered
and spaced as directed.
- 1 point: When the "Create Puzzle" button is clicked, a puzzle
stage is created with the selected dimensions and the size selection
stage is closed.
- 17 points: Puzzle Stage:
- 1 point: The puzzle stage title is "Lights Out".
- 1 point: The puzzle stage is not resizable.
- 1 point: Centered within the center of the border layout is a
button grid with a gray background as specified above.
- 3 points: Each button has a preferred size of 50-by-50 with
radius 10 rounded corners and 1 spacing.
- 1 point: Buttons are initially randomized in such a way that the
puzzle is solvable.
- 1 point: Buttons are either off (black) or on (yellow).
- 3 points: Each button press causes that button and any
immediately adjacent above/below/left/right to be toggled.
- 1 point: The bottom of the border layout contains two buttons
"Randomize" and "Chase Lights" that are centered separated by 20
pixels.
- 2 points: Pressing the "Randomize" button has the effect of
pressing each light button with probability 0.5.
- 2 points: Pressing the "Chase Lights" button has the effect of
following the chasing lights strategy described above.
- 1 point: The application exits when the user clicks on the
red "X" button in the upper-right corner of the puzzle stage window.