Discussion 12: Graphical Applications
In today’s discussion, you’ll get some hands-on practice building a GUI game, Lights Out. In Lights Out, you are presented with a grid of light bulbs, each toggled either on or off in a random arrangement.
The goal is to click on different grid squares so that all of the light bulbs are simultaneously off. Each time that a square is clicked, it toggles (i.e., switches from on to off or from off to on) that light square’s light bulb as well as the light bulbs in any of the orthogonally adjacent squares (i.e., the squares directly above, left of, right of, or below the clicked square). Once you get better at solving the puzzle, you can try to minimize the number of clicks that you need.
Learning Outcomes
- Categorize aspects of a GUI application as part of its model, view, or controller.
- Write code to set up the component hierarchy of a graphical application given a mock-up of its design.
- Write code that performs custom painting on graphical components.
- Write event listeners in graphical applications.
Reminder: Discussion Guidelines
The work that you complete in discussion serves as a formative assessment tool; it offers the opportunity to assess your understanding of the material and for our course staff to get a “pulse” on how things are going, so we can make adjustments in future classes. Your grade in discussion is based on your attendance, active participation, and completion of that day’s activity. More information about the grading and expectations can be found in the syllabus.
Since discussion activities are not graded for correctness, we do not restrict what resources you may use to complete them, which include notes, books, unrestricted conversations with other students, internet searches, and the use of large language models or other generative AI. We advise you to be pragmatic about your use of these resources and think critically about whether they are enhancing your learning. Discussion activities are intended to serve as “strength training” for programming tasks we will expect on assignments and exams (and that you will encounter in future courses and careers), and their main benefit comes from thinking critically to “puzzle” them out.
Your Tasks
Since our treatment of GUIs in CS 2110 is more practical, introducing the basic tools that you need to begin designing graphical applications, we’ll dive right into the code for this discussion. The best way to get better at graphical applications (which tend to be more intricate than many of the other topics that we’ve considered previously) is to play around designing and building them.
Download and read through the starter code. You’ll see that there are 8 TODO tasks for you to complete to finish building the “Lights Out” game. It’s important that you complete these tasks in order so that you can assess your progress at various checkpoints along the way. Rather than using unit tests to verify the correctness of your code (which can be more cumbersome for graphical applications), we encourage you to play-test your game to check that it works.
Creating the Application Window (LightsOutApp.java)
The LightsOutApp class (which extends JFrame) is the main application class. Complete the body of its main() method to construct a new game window and make it visible. Remember to use SwingUtilities.invokeLater() (as we introduced in the lecture) so that the application is started on the event dispatch thread.
When you run this main() method, you should see that a minimal window appears with nothing inside of it (we’ll add widgets to the window in upcoming steps).
When you close this window, it should not terminate the program. Instead, you’ll need to manually terminate the application with the red “Stop” button at the top of IntelliJ.
Creating the Application Window (LightsOutApp.java)
At the top of the LightsOutApp constructor, use instance methods inherited from the JFrame class to set certain properties of the application window (as described in the TODO comment). Look back at the code from Lecture 24 to see which methods may be useful.
When you do this, the window likely will still not be large enough to read its title; we still haven’t added any of its child components. However, you can confirm that the window is no longer resizable and that closing it terminates the application.
Laying Out the Game Components (LightsOutApp.java)
Below the code that you’ve just written, you’ll see starter code that initializes the three child components of the JFrame:
- A
JLabel clicksLabelthat displays the number of light buttons that have been clicked. - A
LightGrid grid(our own custom subtype ofJLabelthat we will turn our attention to soon) that will contain the grid of light buttons. - A
JButton resetButtonthat can be used to reset the game.
Add all of these components to the JFrame in the appropriate positions and ask the layout manager to adjust the sizing and layout of the application window accordingly.
When you run the application now, it will look closer to what we desire.
The clicks label and reset button have appeared, but we can not yet see the light button grid (which we will turn our attention to next). At this point, we have finished the work that we will do in the LightsOutApp class.
Creating the Light Buttons (LightGrid.java)
Within the LightGrid constructor, add code to construct and initialize the grid of buttons. Make sure to follow the instructions in the TODO comment to complete all of the required steps. You should start by selecting an appropriate layout manager for this panel.
Once you have completed this task, rerun the application. You should now see a grid of light buttons, but the bulbs are missing their glass tops and coloring; you’ll complete the custom painting next.
Custom Painting (LightButton.java)
Take a look at the paintComponent() definition within the LightButton class. You’ll see that we’ve done the rather painstaking work to draw the lightbulb bases, threads, and filament within each of the buttons, but we have left the rest of the drawing for you to complete. Follow the instructions in the TODO prompt to complete this painting. You’ll need to select different colors based on the isLit field (part of the model).
When you rerun the application, you should see that all of the bulbs appear to be “off”.
Next, you’ll add the logic to toggle the bulbs.
Toggling the LightButton State (LightButton.java)
Implement the LightButton.toggle() method according to its specifications. Once you have done this, you will have a complete implementation of the LightButton class.
Main Game Logic (LightGrid.java)
The main logic of the game takes place in the LightGrid.toggleNeighborhood() method, which flips the state of the correct light buttons in response to a user’s click. Complete this method according to its specifications. You’ll need to call the toggle() method that you just completed in the previous TODO. It may also help to write an additional private helper method to extract some repeated code and improve maintainability.
Now, when you rerun the application, its view should match the desired appearance. Clicking the reset button should “scramble” up the board state. However, clicking the light buttons will not yet do anything. Completing this part of the controller is your last TODO task.
The Light Button Controller (LightGrid.java)
Note: This last task involves the game’s controller, which is the topic of Thursday’s lecture. Nevertheless, the code that you need to write here is rather short (2 lines) and straightforward, so you should be able to complete this. It may be worth reviewing the game controller after the lecture, once you can appreciate its intricacies.
You’ll find the TODO 8 task inside a lambda expression within the processClick() method. Don’t worry too much about the syntax in this method for right now. This will be the focus of our lecture on Thursday. This processClick() method returns an ActionListener, which describes how the application’s state (its model and view) should be updated in response to an event (clicking a light button).
Add code to the body of this lambda expression to carry out the two steps described in the TODO comment. Make sure that you read through the rest of the class specifications to make sure you are taking care of these steps appropriately.
After you complete this TODO, you should have a finished “Lights Out” game. Verify that the light toggling works as intended and that the label at the top of the frame correctly keeps track of the number of clicks.
How well can you solve the “Lights Out” puzzle? Feel free to update the main() method to try out other grid sizes. You may also need to update the preferred size of the light buttons in the LightButton constructor to make sure the window still fits on your screen.
Submission
At the end of class, your group’s primary author should upload the three code files you worked on to Gradescope:
- LightsOutApp.java
- LightGrid.java
- LightButton.java
Make sure that all other members of the group are tagged on the submission.
Rather than using an autograder for your submission (which is trickier for GUI programs), we’ll read through your code to check your progress.