Spontaneous Saturday 7/18/15: The most situational GUI I’ve ever coded

This is a relatively long post, so I suppose I’ll create a table of contents for it. Just use Ctrl+F (command+F on Mac) to jump down to a particular heading.

  • Introduction (problem statement)
  • GUI
  • Coding process
    • Setting it up
    • Variables
    • Constructor
      • Radio buttons (row 1 columns 1&2)
      • Text fields and checkbox (row 1 columns 3-5)
      • Buttons and labels (row 2)
      • Putting it all together
    • Action listeners
      • RejectButtonListener
      • Other action listeners
  • Closing remarks

 

Introduction (problem statement)

In Pokémon Omega Ruby, I’m on a legendary hunt with standards, meaning my goal is to capture all of the legendaries I can, but at the same time I’m very specific about the nature and three perfect IVs of the legendaries (except Ho-Oh because it’s not worth using without Regenerator). In particular, I’ve been struggling with Reshiram quite a lot, what with its difficult catch rate and the specificity of what I’m looking for. For this battle, I have the following party:

  • Abra, Timid nature with Synchronize
  • Gallade with Galladite and the moves Thunder Wave and False Swipe
  • Hawlucha, HM user
  • Sharpedo, HM user
  • Tentacruel, level 100 for access to Reshiram in the first place (my first ever shiny as well); would be Zekrom, but apparently you can’t trade event Pokémon through the GTS (which is my only safe method of transferring from Y to Omega Ruby)

The HM users are not required, and I would trade Sharpedo for a Pokémon with 120 Speed, but I don’t have one in my immediate possession, so I won’t bother.

My strategy is to lead off with Abra, weaken with Gallade (Brick Break in base form, then Thunder Wave and False Swipe in Mega form), switch to Tentacruel until Reshiram runs out of Fusion Flares, and then use Gallade for the rest of the battle (if applicable). Even if it’s paralyzed at 1 HP, it’s awfully difficult to catch and, once again, I am very particular in what sort of Reshiram I want: Timid with perfect Attack, Special Attack, and Speed. It may seem weird to want perfect Attack with a Timid nature, but it’s required to be able to OHKO Ho-Oh with Life Orb Stone Edge without a Defense-hindering nature. The stats for Attack, Special Attack, and Speed required by my standards are 126, 170, and 121, respectively, and I won’t settle for less.

Because catching Reshiram is so tedious, I decided to use Reshiram’s damage outputs to help evaluate whether to keep trying to capture Reshiram or just reset, for which I used an online damage calculator.

The damage calculator results are as follows:

Calculations against Gallade
Fusion Flare vs. Gallade: 45-54 (67-81 on crit); cannot do 47, 50, or 53 (68, 71, 74, 77, or 80 on crit)
Slash vs. Gallade
• base: 25-30 (38-45 on crit)
• Mega: 19-23 (28-34 on crit)
Extrasensory vs. Gallade: 24-29 (36-43 on crit)
Dragon Breath vs. Gallade: 27-33 (42-49 on crit); cannot do 29 or 32 (44 or 47 on crit)

Calculations against Tentacruel
Fusion Flare: 19-23 (29-34 on crit)
Extrasensory: 42-50 (62-74 on crit); only deals even number damage
Slash: 23-28 (35-42 on crit)
Dragon Breath: 24-28 (34-42 on crit); cannot do 26 (35, 38, or 41 on crit)

I would also keep track of the number of Fusion Flares Reshiram has used so I know when to let Tentacruel faint and sub in Mega Gallade.

However, I found that some of the subtraction between the HP before and the HP after one of Reshiram’s attacks was too bothersome to do in my head, to the point where I felt I had to use a calculator for number crunching. Thursday at 2 a.m. (my curfew as of recently), it dawned on me that maybe I should make a GUI so it would be easier to determine, based on the events of the battle, whether the Reshiram was acceptable or not, in such a way that alleviates the problem of mental math. I was thinking it might take a while and might not save as much time as I’d spend making it, but it just seemed crazy enough at the time that it was something I’d do anyway.

GUI

The day after, using code I created in high school and some resources on the web, I coded the GUI that I had envisioned that night using Eclipse for Java (Luna version because I can’t be bothered to update). I’d say it took me an hour or two, and as situational as it is (especially given the project name “resh”, the class name “Resh”, and the title “time saver?”), it’s a functional and quite useful tool for me.

Resh GUI

The GUI is made up of ten main components in a 2×5 grid:

  • row 1 column 1: radio buttons to select who’s taking the hit
  • row 1 column 2: radio buttons to select Reshiram’s attack
  • row 1 column 3: text field for HP before the attack
  • row 1 column 4: text field for HP after the attack
  • row 1 column 5: checkbox to toggle whether the hit was critical
  • row 2 column 1: button that increments the Fusion Flare counter
  • row 2 column 2: button that sets the Fusion Flare counter to 0
  • row 2 column 3: label (Fusion Flare counter)
  • row 2 column 4: button that changes row 2 column 5 given the information in row 1
  • row 2 column 5: label that tells whether to keep or reject the Reshiram

The default settings for the GUI are theoretical results of the maximum damage dealt from 170 Special Attack Reshiram to Gallade with a critical Fusion Flare (except the Fusion Flare counter starts at 0).

Coding process

Full code: http://pastebin.com/SyZKKAz1

Setting it up

The first part of the coding process, naturally, is importing the necessary tools to build a GUI. This is done using the following series of statements:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

Next, it is important to make sure that the class extends JFrame so that you can create the GUI frame via the constructor. Also, at the end of the class body, it is essential to create a main method that invokes the constructor. For example:

public static void main(String[] args) {
    new Resh();
}

A completely optional step in setup is to “Add default serial version ID”, which is a quick fix to the warning “The serializable class [classname] does not declare a static final serialVersionUID field of type long”, the whole reason why I have the “private static final long serialVersionUID = 1L;” part of my code. The only reason I did this was in an attempt to compress the code into a .jar file, but since it wouldn’t work no matter what I tried, I just gave up.

Variables

In terms of variables, the only ones required for this particular situation are the possible damage values and the components of the GUI.

The damage values are declared as arrays of ints, like so:

private int[] dmgFFG = {45, 46, 48, 49, 51, 52, 54};
private int[] dmgFFGc = {67, 69, 70, 72, 73, 75, 76, 78, 79, 81};
private int[] dmgDBG = {27, 28, 30, 31, 33};
private int[] dmgDBGc = {42, 43, 45, 46, 48, 49};
private int[] dmgXSG = {24, 25, 26, 27, 28, 29};
private int[] dmgXSGc = {36, 37, 38, 39, 40, 41, 42, 43};
private int[] dmgSG = {25, 26, 27, 28, 29, 30};
private int[] dmgSGc = {38, 39, 40, 41, 42, 43};
private int[] dmgSMG = {19, 20, 21, 22, 23};
private int[] dmgSMGc = {28, 29, 30, 31, 32, 33, 34};
private int[] dmgFFT = {19, 20, 21, 22, 23};
private int[] dmgFFTc = {29, 30, 31, 32, 33, 34};
private int[] dmgDBT = {24, 25, 27, 28};
private int[] dmgDBTc = {34, 36, 37, 39, 40, 42};
private int[] dmgXST = {42, 44, 46, 48, 50};
private int[] dmgXSTc = {62, 64, 66, 68, 70, 72, 74};
private int[] dmgST = {23, 24, 25, 26, 27, 28};
private int[] dmgSTc = {35, 36, 37, 38, 39, 40, 41, 42};

The variable names all follow the same format: they start with the three letters “dmg”, followed by a 1 or 2 letter abbreviation of Reshiram’s move (FF for Fusion Flare, DB for Dragon Breath, XS for Extrasensory, S for Slash), a 1 or 2 letter abbreviation of the defender (G for Gallade, MG for Mega Gallade, T for Tentacruel), and an optional lower case “c” denoting critical damage.

For GUI components, I have the following:

private JRadioButton g, mg, t, ff, db, xs, s;
private JPanel pkmn, moves;
private JTextField currentHP, HPafter;
private JCheckBox crit;
private JButton reject, upFFcount, resetFFcount;
private JLabel status, FFcount;

As I said before, the GUI has ten main components in a 2×5 grid. These components are made up of different sub-components like so:

  • row 1 column 1: JRadioButton g, mg, t; JPanel pkmn
  • row 1 column 2: JRadioButton ff, db, xs, s; JPanel moves
  • row 1 column 3: JTextField currentHP
  • row 1 column 4: JTextField HPafter
  • row 1 column 5: JCheckBox crit
  • row 2 column 1: JButton upFFcount
  • row 2 column 2: JButton resetFFcount
  • row 2 column 3: JLabel FFcount
  • row 2 column 4: JButton reject
  • row 2 column 5: JLabel status

That’s the setup; now time to get down to the nitty-gritty.

Constructor

The first thing to do in the constructor is define the properties of the window. For instance:

super("time saver?");
setSize(600,200);

setLayout(new GridLayout(2,5));
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);

“super” sets the title, “setSize” sets the size of the window (first number is width, second number is height), “setLayout” determines the layout of the GUI (in this case, a GridLayout with 2 rows and 5 columns), “setVisible” makes the window visible, and “setDefaultCloseOperation” tells the program what to do when the red X is pressed.

Then come the individual components of the GUI.

Radio buttons (row 1 columns 1&2)

I had to use online resources to figure out radio buttons (and, to some extent, JPanels), which I felt were appropriate in the selection of the defending Pokémon and Reshiram’s moves. Both groups are constructed in the same basic manner, like so:

g = new JRadioButton("Gallade");
g.setSelected(true);
mg = new JRadioButton("Mega Gallade");
t = new JRadioButton("Tentacruel");
 
ButtonGroup pkmnGroup = new ButtonGroup();
pkmnGroup.add(g);
pkmnGroup.add(mg);
pkmnGroup.add(t);
 
pkmn = new JPanel(new GridLayout(3,1));
pkmn.add(g);
pkmn.add(mg);
pkmn.add(t);

The first set of statements initializes the radio buttons, gives them labels, and sets the uppermost one (in this case, base form Gallade) to be selected. The second set of statements lumps the radio buttons into a group, meaning only one button in the group can be selected at a time. The third set of statements creates a sub-panel with a grid layout (in this case, containing 3 rows and 1 column) on which the radio buttons are placed.

Text fields and checkbox (row 1 columns 3-5)

These are relatively simple, although I had to look up how to make a checkbox as well. The text fields follow the same two-line format. Example:

currentHP = new JTextField("225");
currentHP.setHorizontalAlignment(JTextField.CENTER);

The first line creates a text field preset with the number 225 in it, and the second line centers the text in the field.

The checkbox code is a simple three-line procedure:

crit = new JCheckBox("crit");
crit.setHorizontalAlignment(JCheckBox.CENTER);
crit.setSelected(true);

Create a checkbox labeled “crit”, center it, and have it selected by default.

Buttons and labels (row 2)

This part was just applying what I knew from the code I made in high school. Initializing a button in the constructor requires two lines: the first to create the button and the second to add an action listener to it that tells it what to do when clicked. For example:

reject = new JButton("Reject check");
reject.addActionListener(new RejectButtonListener());

Meanwhile, the labels are pretty much the simplest part of the GUI; they display text that can only be updated by the code. When creating a JLabel, there are two parameters you can pass to the JLabel’s constructor: its default text and its alignment in relation to the label. Afterwards, you also have the option of setting the font of the label, which I do for the “status” panel to make its text stand out from the rest of the GUI. For example:

status = new JLabel("KEEP", JLabel.CENTER);
status.setFont(new Font("Impact", Font.PLAIN, 24));

Putting it all together

The last but most important part of building a GUI through the constructor is adding the components to it. Note that order matters when adding components to a grid layout. Illustrated below is an example of the way components are added to a grid layout:

add(pkmn);         //row 1 column 1
add(moves);        //row 1 column 2
add(currentHP);    //row 1 column 3
add(HPafter);      //row 1 column 4
add(crit);         //row 1 column 5
add(upFFcount);    //row 2 column 1
add(resetFFcount); //row 2 column 2
add(FFcount);      //row 2 column 3
add(reject);       //row 2 column 4
add(status);       //row 2 column 5

Basically, it goes from left to right across a row, then goes down to the very left of the next row, and so on and so forth.

That’s all for the GUI design. Now comes the more complex part: action listeners.

Action listeners

As mentioned before, action listeners are what tells a GUI element what to do when clicked. A cookie cutter format for action listeners (which I used for all of mine) is as follows:

public class ActionListenerName implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        ...
    }
}

In this particular case, I applied action listeners to all of the buttons on the GUI, each with varying levels of complexity. The Reject check button is the most complex, FF++ the second-most complex, and Reset FF the simplest. So, I find it best to get the most complex stuff out of the way early.

RejectButtonListener

This action listener is the most complex and tedious part of the code. It starts with declaring two variables like so:

int damage = Integer.parseInt(currentHP.getText())
    - Integer.parseInt(HPafter.getText());
boolean reject = true;

The integer variable “damage” takes the difference of HP before and HP after the attack (the values in the JTextFields), which will be used to determine whether or not to reject the Reshiram. The boolean variable “reject” is true by default, but it will be set to false if the damage output of the attack corresponds to one of the values in its theoretical damage range.

Now comes the tedious part, which involves a series of nested “if” statements. The first “if” statement checks which one of the radio buttons corresponding to Reshiram’s moves is selected, like so:

if (ff.isSelected()) { ... }
else if (db.isSelected()) { ... }
else if (xs.isSelected()) { ... }
else if (s.isSelected()) { ... }

It’s easier to start with the moves rather than the Pokémon because the only difference between Gallade and Mega Gallade in terms of damage taken is that Mega Gallade takes Slash significantly better. So, in terms of which Pokémon is selected, Slash has a threefold “if” statement inside, while every other move has a twofold “if” statement inside. (All moves have statements checking “if (t.isSelected())”, but while Slash checks “if (g.isSelected())” and “if (mg.isSelected())” separately, all other moves check “if (g.isSelected() || mg.isSelected())”, a logical OR.) I should also add that .isSelected() is another function that I learned online.

That’s not even the lowest level of abstraction, though. Every statement inside the “if” statements inside the moves has yet another “if” statement inside that checks “if (crit.isSelected())”, which determines whether the code will check an int array of damage range named with a lowercase “c” (e.g., dmgFFGc). Finally, this is where the guts of the action listener are found. Here is a sample:

for (int i = 0; i < dmgFFGc.length; i++) {
    if (dmgFFGc[i] == damage)
        reject = false;
}

If the compiler reaches one of these “for” loops, it will check the appropriate damage range and see if there is any value equivalent to the supposed damage dealt. If not…well, that’s what the last part of the action listener is there to determine.

The final part is simple:

if (reject)
    status.setText("REJECT");
else
    status.setText("KEEP");

Depending on the resulting value of the boolean variable “reject”, the status label will have its text updated to show whether or not the Reshiram is acceptable based on prior logic.

Other action listeners

I coded IncrementButtonListener, which increases the Fusion Flare counter when the FF++ button is pressed, using the code I made in high school. This action listener declares a String variable named “t” that stores the text of the FFcount JLabel, and the rest of the code is quite simple:

if (t.equals("FF: 0"))
    FFcount.setText("FF: 1");
else if (t.equals("FF: 1"))
    FFcount.setText("FF: 2");
else if (t.equals("FF: 2"))
    FFcount.setText("FF: 3");
else if (t.equals("FF: 3"))
    FFcount.setText("FF: 4");
else if (t.equals("FF: 4"))
    FFcount.setText("FF: 5");

Note that the button does nothing when the label says anything else. This is because Fusion Flare only has 5 PP under ordinary circumstances (meaning Reshiram cannot use it any more than 5 times), so there is no use expanding the “if” statement any further. Note the vital functions called: .equals(String) (returns true if “t” (in this case) is equivalent to the string and false otherwise) and .setText(String) (updates the JLabel).

ResetButtonListener is the simplest of the action listeners, as it literally only contains one line of code:

FFcount.setText("FF: 0");

Yep, that’s all that happens when you click the button. The Fusion Flare counter is updated to its default setting.

Closing remarks

I have to say, explaining code takes a lot longer than creating the code—in fact, I’d say about ten times as long. I suppose I’m just not fit to discuss the processes that go on in my mind. Ah, well. I suppose you could consider Vouiv-review a manner of training that sort of skill.

Reminder of the GUI’s appearance:

Resh GUI

As I mentioned, this tool is definitely useful despite the hour it took me to create it and the ten hours it took me to explain it (mind you, these are approximations; take them with a grain of salt). I could have done other stuff with it, like changing “FF++”, “Reset FF”, and “FF: 0” to be more explanatory in what they do (e.g., “Reshiram used Fusion Flare!”, “Reset Fusion Flares”, and “Fusion Flares: 0”, respectively). I also could have substituted the JTextFields for JPanels with 2×1 grid layouts, the first row explaining what the field is and the second row containing the field (i.e., the first panel would contain “HP before attack” above the text field, and the second panel would contain “HP after attack” above the text field). Also, for the checkbox, I could have used the full term “critical” over the abridged “crit”. Finally, now that I think about it, “currentHP” is an odd variable name for what it’s worth (I’d probably prefer “HPbefore” or something to that effect). But hey, the GUI suits me just fine, and I don’t think the GUI as a whole could possibly be useful for anyone else, so I don’t feel like polishing it any further.

All in all, GUIs like these are fun to make, and this one in particular I feel saves me a lot of trouble in trying to find a Reshiram that meets my standards. Still, it remains the most situational GUI I’ve ever coded.

 

Nowi Wins À la prochaine! (Until next time!)

Advertisements

Feedback is always appreciated!

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s