Hello all,
So I have been scouring the internet trying to find an answer to my question. I usually never have to post a question because someone somewhere has asked it, but I guess there is a first for everything.

I am a VERY intro level, brand spankin new 'programmer' (can't even call me that yet). I have 0 hours of college, just high school diploma, but I read a lot. I have been learning Java programming for about 3 months now and am trying to get an idea of where I am and if I am learning at a slow pace or at an average pace. This is my first programming language, so all the concepts are new to me. I haven't done any other programming.

What I am trying to figure out is "What level am I at" and I suppose I mean that in a college kind of way. So what 'grade' in college would you expect this to come from? Would a day 1 freshman pump this code out easily or is it more sophmore year? Something like that.

These are the concepts that I feel comfortable with (at a beginner level at least):
Variables/data types/arrays all that basic stuff
Loops/logic/conditionals/switch
Classes
Methods
Arguments
The Pass-by-value concept makes sense
Inheritance
Interfaces
Abstract classes/methods
Polymorphism
Exceptions
SOME file I/O and streams. Only the basics as you can see
Some swing knowlege. My game isn't very pretty but it is decent with some graphics
Just started on the whole Generics thing and am just getting into the chapters on collections, some of which I have already used in my code and understand.

The thing with this program that took the most thought was how to make a Variable Length MultiDimensional Array for all the word lists. This is when I started to learn the collections framework. Give me another month and I will have that down solid I beleive.

Now I've got my eye on that "Threads" chapter..... IM SO EXCITED! That stuff looks so cool I cant wait to get into that chapter after collections are done.

So I would just like some feedback. It would be SO very much appreciated. I don't have any professional friends who will give me feedback or let me know what I'm doing wrong. I am sure you can easily tell from my code for my little hangman game that I don't know much at all. I used all very basic concepts to build this and there is nothing fancy. I'm sure a pro could have made this game with half the code or less lol. But to me it's the coolest thing in the entire world because I made it ;)

Ok enough rambling. Can anyone offer some tips/advice/recommendations. What level of college would I be learning this? Freshman year day one? Or have I covered a bit more than that? I know classes tend to move pretty slow sometimes and I am reading my book like 4 hours a day. Literally nothing you say could hurt my feelings so please be blunt. Don't sugar coat anything, that's not what I want. I want straight forward (harsh is better than tactful for me) so I know where I stand. I plan to continue my studying and I WISH I started this years ago. I'm 26 now. I wish I started when I was 12 lol.

Also, final note, all the code for this hangman game is completely original. I did look up some suggestions online when I was stuck on something but I never ended up using what they said, maybe sort of a modified version. But the concepts and design of my game are 100% my own, so all the concepts here are what I understand fully.

Thanks so much everyone.

import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.border.EmptyBorder;
import java.util.ArrayList;
import java.util.Random;

public class Hangman extends JFrame{

    private JButton a;
    private JButton b;
    private JButton c;
    private JButton d;
    private JButton e;
    private JButton f;
    private JButton g;
    private JButton h;
    private JButton i;
    private JButton j;
    private JButton k;
    private JButton l;
    private JButton m;
    private JButton n;
    private JButton o;
    private JButton p;
    private JButton q;
    private JButton r;
    private JButton s;
    private JButton t;
    private JButton u;
    private JButton v;
    private JButton w;
    private JButton x;
    private JButton y;
    private JButton z;
    private JButton[] buttons = new JButton[26];  //add buttons to array so I can loop through them later.
    private JLabel trysRemaining;
    private int setTrysHere = 6; //I used this so you can adjust the number of lives in one spot only.
    private int trys = setTrysHere;
    private int badGuesses = 0;
    private int wins = 0;
    private int losses = 0;
    private JLabel winsLabel = new JLabel("Wins:  " + wins);
    private JLabel lossesLabel = new JLabel("Losses: " + losses);
    private JLabel letters[] = new JLabel[30]; //used to display "_" for each letter. When letter is guessed, it changes to display that letter.
    private String word;  //current word to guess. Pulled from the word list at random (whichever word list is selected by user).
    private Boolean firstGame = true;
    private String[] arr;  //stores the list when converted from ArrayList to a String array.
    private JPanel contentPane;
    private ArrayList<String> words = new ArrayList<String>();  //holds the list selected by the user. Turned into 'arr[]' later on for working on.
    private ListSelector frame; //refers to the ListSelector window. I needed a 'global' like variable to reference the two windows.
    private Hangman hang;  //refers to this object. I am sure there was a better way to do this but this is all I could come up with lol.
    private ButtonGroup difficultyGroup = new ButtonGroup();
    private String lastDifficulty = "Medium";
    private JRadioButtonMenuItem rbEasy;  //these specific options needed to be declared up here so that I could use them inside another handler class later.
    private JRadioButtonMenuItem rbMedium;
    private JRadioButtonMenuItem rbHard;
    private JRadioButtonMenuItem rbNoMercy;
    private JLabel currentDifficulty = new JLabel("Current Difficulty:  " + lastDifficulty);
    private JLabel victim;
    private Boolean drawings = true;
    private BufferedImage hang0;
    private BufferedImage hang1;
    private BufferedImage hang2;
    private BufferedImage hang3;
    private BufferedImage hang4;
    private BufferedImage hang5;
    private BufferedImage hang6;
    private BufferedImage hang7;
    private BufferedImage hang8;
    private BufferedImage hang9;
    private BufferedImage hang10;
    private Color green = new Color(20,110,30);
    private Color yellow = new Color(155,155,0);
    private Color red = new Color(200,0,0);


    public Hangman(){
        super("Hangman");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(300, 300, 620, 300);

        //create the menu.///////////add all action listeners as abstract classes within this section//////////
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        JMenu menuHangman = new JMenu("Hangman");
        menuBar.add(menuHangman);

        JMenu menuDifficulty = new JMenu("Difficulty");
        menuHangman.add(menuDifficulty);

        DifficultyHandler diffHandle = new DifficultyHandler();

        rbEasy = new JRadioButtonMenuItem("Easy");
        difficultyGroup.add(rbEasy);
        menuDifficulty.add(rbEasy);
        rbEasy.addActionListener(diffHandle);

        rbMedium = new JRadioButtonMenuItem("Medium");
        difficultyGroup.add(rbMedium);
        menuDifficulty.add(rbMedium);
        rbMedium.setSelected(true);
        rbMedium.addActionListener(diffHandle);

        rbHard = new JRadioButtonMenuItem("Hard");
        difficultyGroup.add(rbHard);
        menuDifficulty.add(rbHard);
        rbHard.addActionListener(diffHandle);

        rbNoMercy = new JRadioButtonMenuItem("No Mercy");
        difficultyGroup.add(rbNoMercy);
        menuDifficulty.add(rbNoMercy);
        rbNoMercy.addActionListener(diffHandle);

        JMenuItem menuItemChangeList = new JMenuItem("Change List");
        menuHangman.add(menuItemChangeList);
        menuItemChangeList.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                hang.setVisible(false);
                frame.setVisible(true);
            }
        });

        JMenuItem menuItemNewWord = new JMenuItem("New Word");
        menuHangman.add(menuItemNewWord);
        menuItemNewWord.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                int option = JOptionPane.showConfirmDialog(null, "This will give you a loss. Are you sure?", "Quitter!", JOptionPane.OK_CANCEL_OPTION);
                if(option == JOptionPane.OK_OPTION){
                    resetGame();
                    newWord();
                    losses++;
                    lossesLabel.setText("Losses: " + losses);
                }
            }
        });

        JMenuItem menuItemResetStats = new JMenuItem("Reset Stats");
        menuHangman.add(menuItemResetStats);
        menuItemResetStats.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                int option = JOptionPane.showConfirmDialog(null, "Are you sure you want to reset your stats?\n" +
                        "(this will not reset the current word)", "Reset Stats?", JOptionPane.OK_CANCEL_OPTION);
                if(option == JOptionPane.OK_OPTION){
                    wins = 0;
                    losses = 0;
                    winsLabel.setText("Wins: " + wins);
                    lossesLabel.setText("Losses: " + losses);
                }
            }
        });

        JMenuItem menuItemExit = new JMenuItem("Exit");
        menuHangman.add(menuItemExit);
        //end menu creation./////////////////

        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);  //use absolute positioning for all frames.

        //initialize each button. I add them to a button array so I can work on them easily later.
        a = new JButton("a");
        buttons[0] = a;
        b = new JButton("b");
        buttons[1] = b;
        c = new JButton("c");
        buttons[2] = c;
        d = new JButton("d");
        buttons[3] = d;
        e = new JButton("e");
        buttons[4] = e;
        f = new JButton("f");
        buttons[5] = f;
        g = new JButton("g");
        buttons[6] = g;
        h = new JButton("h");
        buttons[7] = h;
        i = new JButton("i");
        buttons[8] = i;
        j = new JButton("j");
        buttons[9] = j;
        k = new JButton("k");
        buttons[10] = k;
        l = new JButton("l");
        buttons[11] = l;
        m = new JButton("m");
        buttons[12] = m;
        n = new JButton("n");
        buttons[13] = n;
        o = new JButton("o");
        buttons[14] = o;
        p = new JButton("p");
        buttons[15] = p;
        q = new JButton("q");
        buttons[16] = q;
        r = new JButton("r");;
        buttons[17] = r;
        s = new JButton("s");
        buttons[18] = s;
        t = new JButton("t");
        buttons[19] = t;
        u = new JButton("u");
        buttons[20] = u;
        v = new JButton("v");
        buttons[21] = v;
        w = new JButton("w");
        buttons[22] = w;
        x = new JButton("x");
        buttons[23] = x;
        y = new JButton("y");
        buttons[24] = y;
        z = new JButton("z");
        buttons[25] = z;

        //add buttons to screen in proper locations. Two loops for two rows.
        int xLoc = 10;
        for(int i=0; i<13; ++i){
            buttons[i].setBounds(xLoc, 176, 45, 23);
            contentPane.add(buttons[i]);
            xLoc += 45;
        }

        xLoc = 10;
        for(int i=13; i<26; ++i){
            buttons[i].setBounds(xLoc, 204, 45, 23);
            contentPane.add(buttons[i]);
            xLoc += 45;
        }

        //display remaining attempts. Decrement each time you miss a letter.
        trysRemaining = new JLabel("Trys Remaining:   " + trys);
        trysRemaining.setBounds(385, 70, 150, 20);
        contentPane.add(trysRemaining);
        trysRemaining.setFont(new Font("comic sans ms", Font.BOLD, 14));
        trysRemaining.setForeground(green);

        //display wins and losses. Resets when program ends.
        winsLabel.setBounds(385, 10, 75, 20);
        contentPane.add(winsLabel);
        winsLabel.setFont(new Font("comic sans ms", Font.BOLD, 14));
        lossesLabel.setBounds(485, 10, 75, 20);
        contentPane.add(lossesLabel);
        lossesLabel.setFont(new Font("comic sans ms", Font.BOLD, 14));

        currentDifficulty.setBounds(385, 40, 200, 20);
        contentPane.add(currentDifficulty);
        currentDifficulty.setFont(new Font("comic sans ms", Font.BOLD, 14));


        //initialize 30 JLabels (30 is the max length for a word in this version).
        //these will later be added to the frame and each time a word is loaded, the corresponding JLabels will be '_' by default,
        //and when the user guesses a correct letter, all occurrences of that letter will change from '_' to the letter.
        for(int i=0; i<30; ++i){
            letters[i] = new JLabel();
        }
        HandlerClass handler = new HandlerClass();  
        for(int i=0; i<26; ++i){
            buttons[i].addActionListener(handler);
        }   
        try{
            hang0 = ImageIO.read(new File("src\\Pics\\hang0.gif"));
            hang1 = ImageIO.read(new File("src\\Pics\\hang1.gif"));
            hang2 = ImageIO.read(new File("src\\Pics\\hang2.gif"));
            hang3 = ImageIO.read(new File("src\\Pics\\hang3.gif"));
            hang4 = ImageIO.read(new File("src\\Pics\\hang4.gif"));
            hang5 = ImageIO.read(new File("src\\Pics\\hang5.gif"));
            hang6 = ImageIO.read(new File("src\\Pics\\hang6.gif"));
            hang7 = ImageIO.read(new File("src\\Pics\\hang7.gif"));
            hang8 = ImageIO.read(new File("src\\Pics\\hang8.gif"));
            hang9 = ImageIO.read(new File("src\\Pics\\hang9.gif"));
            hang10 = ImageIO.read(new File("src\\Pics\\hang10.gif"));
            drawings = true;
        }catch(Exception e){
            JOptionPane.showMessageDialog(null, "The picture files are corrupt, so you won't see the drawings");
            drawings = false;
        }

        //create victim label to display victim (if possible)
        victim = new JLabel();
        victim.setBounds(150, 10, 200, 200);
        contentPane.add(victim);
        drawVictim();
    }

    //handler class for the difficulty level option buttons. 
    public class DifficultyHandler implements ActionListener{
        public void actionPerformed(ActionEvent e){

            String action = e.getActionCommand();
            Boolean fresh = true;  //if you have already guessed a letter for the current word, this will be false.
            for(int i=0; i<26; ++i){  //(have you guessed anything yet? you can tell by checking for disabled buttons)
                if(!buttons[i].isEnabled()){
                    fresh = false;
                }
            }
            int option = JOptionPane.showConfirmDialog(null, "Are you sure you want to change difficulty?\n" +
                    "If you have already guessed a letter for this word\n" +
                    "then you will get a loss. If you haven't guessed\n" +
                    "yet then you will not get a loss", "Change Difficulty?", JOptionPane.OK_CANCEL_OPTION);
            if(option == JOptionPane.OK_OPTION){ //if they are sure, continue.
                switch (action){
                case "Easy":
                    resetGame();
                    newWord();
                    setTrysHere = 10;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "Easy";
                    trysRemaining.setForeground(green);
                    break;
                case "Medium":
                    resetGame();
                    newWord();
                    setTrysHere = 6;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "Medium";
                    trysRemaining.setForeground(green);
                    break;
                case "Hard":
                    resetGame();
                    newWord();
                    setTrysHere = 4;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "Hard";
                    trysRemaining.setForeground(yellow);
                    break;
                case "No Mercy":
                    resetGame();
                    newWord();
                    setTrysHere = 1;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "No Mercy";
                    trysRemaining.setForeground(red);
                    break;
                }
                if(!fresh){  //if they already guessed something, give them a loss.
                    losses++;
                    lossesLabel.setText("Losses: " + losses);
                }
            }else{  //if they hit cancel then you need to set the selected option button back to where it was before the click. This is why i needed the option buttons to be class level variables instead of being declared inside the constructor like the rest of the menu was.
                switch (lastDifficulty){
                case "Easy":
                    rbEasy.setSelected(true);
                    break;
                case "Medium":
                    rbMedium.setSelected(true);
                    break;
                case "Hard":
                    rbHard.setSelected(true);
                    break;
                case "No Mercy":
                    rbNoMercy.setSelected(true);
                    break;
                }
            }
        }
    }

    //the setFrame and setHang are how I access the windows for the ListSelector and Hangman instances. Is there a better way to do this or is this ok? 
    public void setFrame(ListSelector e){
        frame = e;
    }

    public void setHang(Hangman e){
        hang = e;
    }

    //used to set the current word list to whatever the user selected in the ListSelector's comboBox.
    public void setList(ArrayList<String> list){
        words = list;
    }

    //does a lot of stuff to set the next word to be guessed.
    public String newWord(){
        //reset the labels after the game is over so you can reset them for the next word.
        if(!firstGame){
            for(int i=0; i<word.length(); ++i){
                letters[i].setText("");
            }
        }

        //set the current list to whatever the user has selected in the ListSelector.
        setList(frame.getWordsList());

        //choose a random number then use the corresponding word in the current word list.
        arr = words.toArray(new String[0]);
        Random rand = new Random();
        int n = rand.nextInt(arr.length);
        word = arr[n].toLowerCase();

        //create a label array with a length of word.length() and show a '_' for each letter until guessed.
        //xPos is used to line them up 10 pixels away from eachother.
        Font labelFont = new Font("courier", Font.BOLD, 20);
        int xPos = 300; 
        for(int i=0; i<word.length(); ++i){
            if(word.charAt(i)!=' '){
                letters[i].setFont(labelFont);  //must use monospaced font otherwise wide letters like 'm' and 'w' will crowd the other letters.
                letters[i].setText("_");
            }else{
                letters[i].setFont(labelFont);
                letters[i].setText(" "); //if there is a space, then that slot is set to " ". Sort of gives it away for the states but it's either that or I combine the words like 'newyork' which looks stupid.
            }
            letters[i].setBounds(xPos, 131, 55, 30);
            contentPane.add(letters[i]);
            xPos += 15;
        }
        firstGame = false;;
        return word;
    }

    public void drawVictim(){
        //if(drawings) is there so that if any of the image files failed to load then the game will just continue without the drawing of the victim.
        //this method checks diffictulty and draws the images based on difficulty && number of bad guesses. They were set up so the drawing was even with number of guesses.
        if(drawings){
            switch (lastDifficulty){
            case "Easy":
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(hang0));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(hang1));
                    break;
                case 2:
                    victim.setIcon(new ImageIcon(hang2));
                    break;
                case 3:
                    victim.setIcon(new ImageIcon(hang3));
                    break;
                case 4:
                    victim.setIcon(new ImageIcon(hang4));
                    break;
                case 5:
                    victim.setIcon(new ImageIcon(hang5));           
                    break;
                case 6:
                    victim.setIcon(new ImageIcon(hang6));
                    trysRemaining.setForeground(yellow);
                    break;
                case 7:
                    victim.setIcon(new ImageIcon(hang7));
                    break;
                case 8:
                    victim.setIcon(new ImageIcon(hang8));
                    trysRemaining.setForeground(Color.RED);
                    break;
                case 9:
                    victim.setIcon(new ImageIcon(hang9));
                    break;
                case 10:
                    victim.setIcon(new ImageIcon(hang10));
                    break;
                }
                break;

            case "Medium":
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(hang0));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(hang1));
                    break;
                case 2:
                    victim.setIcon(new ImageIcon(hang2));
                    trysRemaining.setForeground(yellow);
                    break;
                case 3:
                    victim.setIcon(new ImageIcon(hang4));
                    break;
                case 4:
                    victim.setIcon(new ImageIcon(hang6));
                    trysRemaining.setForeground(red);
                    break;
                case 5:
                    victim.setIcon(new ImageIcon(hang8));
                    break;
                case 6:
                    victim.setIcon(new ImageIcon(hang10));
                    break;
                }
                break;

            case "Hard":
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(hang0));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(hang1));
                    break;
                case 2:
                    victim.setIcon(new ImageIcon(hang2));
                    trysRemaining.setForeground(red);
                    break;
                case 3:
                    victim.setIcon(new ImageIcon(hang6));
                    break;
                case 4:
                    victim.setIcon(new ImageIcon(hang10));
                    break;
                }
                break;

            case "No Mercy":
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(hang0));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(hang10));
                    break;
                }
                break;
            }
        }

    }

    //one handler class for all the letter buttons.
    public class HandlerClass implements ActionListener{
        public void actionPerformed(ActionEvent e){
            Character guess = e.getActionCommand().charAt(0); //set guess to the character of the button pressed.
            Boolean done = true;                            //used later to see if there is a '_' left so I know if word is still not guessed completely.
            JButton source = (JButton)e.getSource();
            source.setEnabled(false);                         //used to disable the button after pressing it.
            //check to see if the word even contains the string, so if it doesn't you don't waste time looping and searching.
            //this if statement isn't needed, but I figured it would save some (surely insignificant) loop time. Do you think so?
            if(word.contains(guess.toString())){
                for(int i=0; i<word.length(); ++i){   //if it does, find each location and change it to the letter guessed.
                    if(word.charAt(i) == guess){
                        letters[i].setText(guess.toString());
                    }
                }
            }else{
                --trys;
                ++badGuesses;
                trysRemaining.setText("Trys Remaining:   " + trys);
                drawVictim();
                if(trys==0){
                    JOptionPane.showMessageDialog(null, "YOU'RE OUT OF CHANCES! \n" +
                            "The word was \"" + word + "\". \n" +
                            "Try to get the next one right!");
                    ++losses;
                    lossesLabel.setText("Losses: " + losses);
                    resetGame();
                    newWord();
                }
            }
            //if any '_' are left then you haven't guessed all the letters yet.
            for(int i=0; i<word.length(); ++i){
                if(letters[i].getText().equals("_")){
                    done = false;
                }
            }
            //if there are no '_' left then the word is guessed. Congratulate user, update stats and reset game.
            if(done==true){
                JOptionPane.showMessageDialog(null, "Congratulations! You guessed the word!\n" +
                        "Keep it going!");
                ++wins;
                winsLabel.setText("Wins: " + wins);
                resetGame();
                newWord();
            }
            done = true;
        }
    }

    //when game is won or lost, this is called to reset everything for next game.
    public void resetGame(){
        for(int i=0; i<26; ++i){
            buttons[i].setEnabled(true);
        }
        trys = setTrysHere;
        trysRemaining.setText("Trys Remaining:   " + trys);
        badGuesses = 0;
        victim.setIcon(new ImageIcon(hang0));

        //reset the color of the trysRemaining label accordingly.
        switch (lastDifficulty){
        case "Easy":
            trysRemaining.setForeground(green);
            break;
        case "Medium":
            trysRemaining.setForeground(green);
            break;
        case "Hard":
            trysRemaining.setForeground(yellow);
            break;
        case "No Mercy":
            trysRemaining.setForeground(red);
            break;
        }

    }
}


import java.awt.EventQueue;
import java.awt.Frame;
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.ButtonGroup;

public class ListSelector extends JFrame {

    private JPanel contentPane;
    private ArrayList<ArrayList<String>> words = new ArrayList<ArrayList<String>>();  //an ArrayList of ArrayLists (multi-dimensional ArrayList). This was the hardest part of this entire program for me. Took me a while to figure it out. This way you can add text files to the game for more words lists. This allows a variable amount of lists with a variable amount of words. 
    private ArrayList<String> names = new ArrayList<String>(); //holds the names of each text file in the Words List directory (without the .txt). Used to populate the multi-dimensional ArrayList.
    private JComboBox<String> comboBox;
    private ArrayList<String> wordsList = new ArrayList<String>();  //this stores the list that is SELECTED from the comboBox to send to the game.
    private Boolean badWord = false;  //used to check if a word contains anything other than a letter.
    private Boolean firstRun = true;
    private int lastSelected = 0; //if you click "change list" and then click "cancel" then this will be used to set the comboBox selection back to where it was. If you DO change the list then this is set to the new list for next time.
    public static ListSelector frame;  //i needed these to be global but can't in java so I declared them here in both classes and defined set/get methods in both classes to set the references so they act like global variables.
    public static Hangman hang;  //same as above.
    private final ButtonGroup difficultyGroup = new ButtonGroup();

    public static void main(String[] args) {
        frame = new ListSelector();
        frame.setVisible(true);
    }

    public ListSelector() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(400, 400, 450, 330);
        setResizable(false);

        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        comboBox = new JComboBox<String>();
        comboBox.setBounds(30, 89, 380, 22);
        contentPane.add(comboBox);

        JLabel lblNewLabel = new JLabel("Select the category you would like to play:");
        lblNewLabel.setBounds(30, 51, 239, 27);
        contentPane.add(lblNewLabel);

        JButton btnLetsPlay = new JButton("Lets Play!");
        btnLetsPlay.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if(firstRun){
                    frame.setVisible(false);
                    wordsList = new ArrayList<String>(words.get(comboBox.getSelectedIndex())); //get the selected list from the comboBox.
                    hang = new Hangman(); //calls constructor only once. After this it is referenced as the only instance of Hangman.
                    hang.setLocation(400, 400);
                    hang.setResizable(false);
                    firstRun = false;
                    hang.setFrame(frame);
                    hang.setHang(hang);
                    hang.newWord();
                    hang.setVisible(true);
                    lastSelected = comboBox.getSelectedIndex(); //sets lastSelected so that if you hit cancel later the comboBox will revert back to where it was before.
                }else{
                    frame.setVisible(false); 
                    hang.resetGame();
                    wordsList = new ArrayList<String>(words.get(comboBox.getSelectedIndex()));
                    hang.newWord();
                    hang.setVisible(true);
                    lastSelected = comboBox.getSelectedIndex();
                }
            }
        });
        btnLetsPlay.setBounds(319, 239, 91, 23);
        contentPane.add(btnLetsPlay);

        JButton btnCancel = new JButton("Cancel");
        btnCancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(firstRun){
                    System.exit(0);  //on first run, if you hit cancel it just closes the program. 
                }else{
                    frame.setVisible(false);
                    hang.setVisible(true);
                    comboBox.setSelectedIndex(lastSelected); //set the comboBox selection back to where it was before you changed it.
                }
            }
        });
        btnCancel.setBounds(208, 239, 91, 23);
        contentPane.add(btnCancel);

        FolderList();

    }
    //Hangman and ListSelector both have the same variables to refer to themselves and each other. So they act as global variables (is this bad practice or is it ok? It's the only thing I could think of.)
    public Hangman getHang(){ //this is how I get the references.
        return hang;
    }

    public ArrayList<String> getWordsList(){
        return wordsList;
    }

    public void FolderList(){

        //open the Word Lists file and get the name of every file in there (without the .txt). ONLY gets .txt files, all others are ignored.
        //if the .txt file doesn't contain any eligible words (less than 21 characters, no special characters, can contain spaces) then that file is ignored.
        try{
            File folder = new File("src\\Word Lists");
            File[] listOfFiles = folder.listFiles();

            //this entire for loop loops through each .txt file and as long as the file contains one eligible word then it is added to the list.
            for (int i = 0; i < listOfFiles.length; i++) {
                if (listOfFiles[i].isFile()) {
                    int j = listOfFiles[i].toString().lastIndexOf("\\");
                    if(listOfFiles[i].toString().endsWith("txt")){
                        Scanner sc = new Scanner(new File(listOfFiles[i].toString()));
                        Boolean okWord = false; //very important line. initially set to false, but if the while loop finds at least one eligible word then it is set to true so that the list can be added to the names ArrayList.
                        while(sc.hasNext()){    //this is important because the program will fail if it encounters a situation where it doesn't have any words to work with. Run-time error. Wouldn't be found at compile time.
                            String currentWord = sc.nextLine();
                            if(currentWord.length()<=21 && currentWord.length()>1){
                                for(int k=0; k<currentWord.length(); ++k){
                                    if(Character.isLetter(currentWord.charAt(k)) || Character.isSpaceChar(currentWord.charAt(k))){
                                        okWord = true;
                                    }else{
                                        okWord = false; 
                                        break; //if the current character of the current word is bad, then break the loop and move to the next word right away so that okWord isn't set to true when the next character is a letter or space.
                                    }
                                }
                            }
                        }
                        sc.close();
                        if(okWord){
                            names.add(listOfFiles[i].toString().substring(j+1, listOfFiles[i].toString().length()-4));
                        }
                    }
                }
            }

            //if there were no files with at least 1 eligible word then exit program, telling user to add some word lists.
            if(names.toArray().length==0){
                JOptionPane.showMessageDialog(null, "No word lists with eligible words were found!\n" +
                        "You can add word list .txt files to the \"src\\WordLists\" file.\n" +
                        "The format is one word per line, no special characters (spaces ok)\n" +
                        "and each word must be 20 characters or less.");
                System.exit(0);
            }

            //add all the names (without .txt) to the comboBox for selection.
            for(int i=0; i < names.toArray().length; ++i){
                comboBox.addItem(names.toArray()[i].toString());
            }

        }catch(Exception e){
            JOptionPane.showMessageDialog(null, "There seems to be an issue with the Word Lists file.\n" +
                    "Word lists must be in the \"src\\Word Lists\" folder.\n" +
                    "Program will now close.");
            System.exit(0);
        }

        //opens each .txt file one at a time, adding an ArrayList to the ArrayList for each one. 
        //adds all the words to the corresponding ArrayList within the main ArrayList.
        //so 'words[0][0]' is the first word of the first list and so on.
        //only adds words that have letters and spaces only, no numbers or other signs.
        try{
            for(int i=0; i<names.toArray().length; ++i){
                Scanner sc = new Scanner(new File("src\\Word Lists\\" + names.toArray()[i].toString() + ".txt"));
                words.add(new ArrayList<String>());
                while(sc.hasNext()){
                    String currentWord = sc.nextLine();
                    if(currentWord.length()<=21 && currentWord.length()>1){
                        for(int j=0; j<currentWord.length(); ++j){
                            if(!Character.isLetter(currentWord.charAt(j)) && !Character.isSpaceChar(currentWord.charAt(j))){
                                badWord = true;
                            }
                        }
                        if(!badWord){
                            words.get(i).add(currentWord);
                        }
                    }
                    badWord = false;
                }
                sc.close();
            }
        }catch(Exception e){
            JOptionPane.showMessageDialog(null, "There seems to be an issue with the Animals.txt");
            e.printStackTrace();
            System.exit(0);
        }
    }
}

Edited 3 Years Ago by salomonsk8: Adding that the code is original and not copied at all.

I didn't realize how long that code was. If that's too long to be posting on here let me know and I will remove it. Or take out half or something. I'm a new poster so let me know.

Also, I would like to add that I understand that there is so much more to programming than just data structures and loops and understanding how to make simple things like a dumb hangman game. I know that I will later on need to focus more on design concepts and how to work on a team, working with customers that change their mind, presenting a prototype to a client, having a limited budget and time and deadlines, how to see the 'big picture' of a program and knowing what future features/compatability it might need. I understand that the things I have learned so far are simple and are just the actual 'typing' part. The real programming is the design, portability, and beauty of your code.

Just wanted to say that before someone said that as their answer. This is why I said "you cant even call me a programmer yet" lol.

Edited 3 Years Ago by salomonsk8

:-) Yeah, asking us to analyze almost a thousand lines of code is a BIT excessive... :lol: Try to apply the KISS principal first, and then after you restructure your code, post again. :-)

That said, you are, to my mind at least, making good progress for a newbie at programming. Give yourself another year, and I think you might make a decent java programmer. Do remember that programming is NOT the same as software engineering. Work on studying the fundamentals, not just the coding constructs. See the books by Donald Knuth on software engineering as an example. They (all 4 volumes) are published under the title "The Art of Computer Programming". Also, another great one is Niklas Wirth's "Algorithms + Data Structures = Programs". Wirth was the author of both Pascal and Modula programming languages, and the graduate advisor of my college buddy (and still best friend) Bruce Ravenel - one of the co-inventors of the Intel x86 processor family.

what level you are? ehm, I would say the level of a first-year (maybe second year) college student who takes a programming course in Java.

In the list you've provided, there's nothing but very basic concepts, so no real fuss there. also, the code you've shown, once decently refactored, might be a few hundres of lines of code less, not to mention easier to read.

a few examples:

a little snippet from your code:

switch (lastDifficulty){
            case "Easy":
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(hang0));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(hang1));
                    break;
                case 2:
                    victim.setIcon(new ImageIcon(hang2));
                    break;
                case 3:
                    victim.setIcon(new ImageIcon(hang3));
                    break;
                case 4:
                    victim.setIcon(new ImageIcon(hang4));
                    break;
                case 5:
                    victim.setIcon(new ImageIcon(hang5));           
                    break;
                case 6:
                    victim.setIcon(new ImageIcon(hang6));
                    trysRemaining.setForeground(yellow);
                    break;
                case 7:
                    victim.setIcon(new ImageIcon(hang7));
                    break;
                case 8:
                    victim.setIcon(new ImageIcon(hang8));
                    trysRemaining.setForeground(Color.RED);
                    break;
                case 9:
                    victim.setIcon(new ImageIcon(hang9));
                    break;
                case 10:
                    victim.setIcon(new ImageIcon(hang10));
                    break;
                }
                break;
            case "Medium":
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(hang0));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(hang1));
                    break;
                case 2:
                    victim.setIcon(new ImageIcon(hang2));
                    trysRemaining.setForeground(yellow);
                    break;
                case 3:
                    victim.setIcon(new ImageIcon(hang4));
                    break;
                case 4:
                    victim.setIcon(new ImageIcon(hang6));
                    trysRemaining.setForeground(red);
                    break;
                case 5:
                    victim.setIcon(new ImageIcon(hang8));
                    break;
                case 6:
                    victim.setIcon(new ImageIcon(hang10));
                    break;
                }
                break;
                ....
                }

can you see how that's ... well, not really an optimal way?
you can do the same with fewer lines of code.

ImageIcon[] icons = new ImageIcon[10];
int whenToYellow = 0;
int whenToRed = 0;
switch (lastDifficulty){
            case "Easy":
                icons = {new ImageIcon(hang0), new ImageIcon(hang1), .., new ImageIcon(hang10)};
                whenToYellow = 6;
                whenToRed = 8;
                break;
            case "Medium":
                icons = {new ImageIcon(hang0), new ImageIcon(hang1), new ImageIcon(hang2),new ImageIcon(hang4), new ImageIcon(hang6), new ImageIcon(hang8), new ImageIcon(hang10)};                
                whenToYellow = 2;
                whenToRed = 4;
                break;
        }

            victim.setIcon(icons[badGuesses]);
            if ( badGuesses == whenToYellow)
                trysRemaining.setForeground(yellow);
            else if ( badGuesses == whenToRed)
                trysRemaining.setForeground(red);

lesser code and easier to read.

in this code snippet:

switch (lastDifficulty){
        case "Easy":
            trysRemaining.setForeground(green);
            break;
        case "Medium":
            trysRemaining.setForeground(green);
            break;
        case "Hard":
            trysRemaining.setForeground(yellow);
            break;
}

you could 've used the waterfall principle. when the case is found, it 'll run until the end of the Switch block, or until the next break. it doesn't necessarily stop because a new case is encountered.
so this is the way to go if for several values you have to perform an identical action.

switch (lastDifficulty){
        case "Easy":
        case "Medium":
            trysRemaining.setForeground(green);
            break;
        case "Hard":
            trysRemaining.setForeground(yellow);
            break;
}

another thing to remember: avoid repeating code. like in this snippet:

switch (action){
                case "Easy":
                    resetGame();
                    newWord();
                    setTrysHere = 10;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "Easy";
                    trysRemaining.setForeground(green);
                    break;
                case "Medium":
                    resetGame();
                    newWord();
                    setTrysHere = 6;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "Medium";
                    trysRemaining.setForeground(green);
                    break;
                case "Hard":
                    resetGame();
                    newWord();
                    setTrysHere = 4;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "Hard";
                    trysRemaining.setForeground(yellow);
                    break;
                case "No Mercy":
                    resetGame();
                    newWord();
                    setTrysHere = 1;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = "No Mercy";
                    trysRemaining.setForeground(red);
                    break;
                }

actually, all these case blocks are performing the same operation, only three variables might be different. so, why not put this in a seperate method?

public void performStart(int nrOfTrys, String difficulty, Color color){
    resetGame();
    newWord();
    setTrysHere = nrOfTrys;
    trys = setTrysHere;
    trysRemaining.setText("Trys remaining:  " + trys);
    lastDifficulty = difficulty;
    trysRemaining.setForeground(color);
}

This way, you can reduce your switch-case structure to:

switch (action){
                case "Easy":
                    performStart(10, action, green);
                    break;
                case "Medium":
                    performStart(6, action, green);
                    break;
                case "Hard":
                    performStart(4, action, yellow);
                    break;
                case "No Mercy":
                    performStart(1, action, red);
                    break;
                }

also: something I would do different, I would not create an anonymous implementation of an ActionListener, I would have the class in which my components to which I want to add it implement actionListener, and use it a bit like this:

JButton button1 = new JButton("1");
button1.addActionListener(this);
JButton button2 = new JButton("2");
button2.addActionListener(this);

combined with:

@Override
public void actionPerformed(ActionEvent event){
  if ( event.getSource() == button1){
    // perform actions for button1
  }
  else if ( event.getSource() == button2){
    // perform actions for button2
  }
}

lesser code, and with an easier structure means it'll be a lot easier to maintain afterwards. might be I've overlooked some things you could also refactor, but am on limited time, might take a look at it later.

Comments
Great advice thank you!

One piece of advice I can give is that, whenever you find yourself using several variables of the form nameN, consider refactoring them as an array or an ArrayList. The hang0, hang1, hang2,... hang10 variables in particular stand out as something where a number of things would be simplified if you used an array for them.

Similarly, given that you are just using them to initialize an array, there is no need to have the individual letter buttons declared separately. If you really need to be able to refer to them by the letters themselves - which I don't believe you ever do - you could easily use a HashMap for that purpose instead:

private HashMap<char, JButton> letterButtons = new HashMap<char, JButton();

// later, initialize the HashMap like so
for (char letter = 'a'; letter <= 'z'; letter++) {
    letterButtons.put(letter, new JButton(letter);
}

Note also that you cannot use a String as the index of a swich() statement; you can, however, use an enum, which I have done in the following version of your code:

import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.border.EmptyBorder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

enum ActionType {Easy, Medium, Hard, NoMercy};

public class Hangman extends JFrame {

    private HashMap<Character, JButton> letterButtons = new HashMap<Character, JButton>();
    private JLabel trysRemaining;
    private int setTrysHere = 6; //I used this so you can adjust the number of lives in one spot only.
    private int trys = setTrysHere;
    private int badGuesses = 0;
    private int wins = 0;
    private int losses = 0;
    private JLabel winsLabel = new JLabel("Wins:  " + wins);
    private JLabel lossesLabel = new JLabel("Losses: " + losses);
    private JLabel letters[] = new JLabel[30]; //used to display "_" for each letter. When letter is guessed, it changes to display that letter.
    private String word;  //current word to guess. Pulled from the word list at random (whichever word list is selected by user).
    private Boolean firstGame = true;
    private String[] arr;  //stores the list when converted from ArrayList to a String array.
    private JPanel contentPane;
    private ArrayList<String> words = new ArrayList<String>();  //holds the list selected by the user. Turned into 'arr[]' later on for working on.
    private ListSelector frame; //refers to the ListSelector window. I needed a 'global' like variable to reference the two windows.
    private ButtonGroup difficultyGroup = new ButtonGroup();
    private ActionType lastDifficulty = ActionType.Medium;
    private JRadioButtonMenuItem rbEasy;  //these specific options needed to be declared up here so that I could use them inside another handler class later.
    private JRadioButtonMenuItem rbMedium;
    private JRadioButtonMenuItem rbHard;
    private JRadioButtonMenuItem rbNoMercy;
    private JLabel currentDifficulty = new JLabel("Current Difficulty:  " + lastDifficulty);
    private JLabel victim;
    private Boolean drawings = true;
    private BufferedImage[] body = new BufferedImage[10]; 
    private Color green = new Color(20,110,30);
    private Color yellow = new Color(155,155,0);
    private Color red = new Color(200,0,0);


    public Hangman(){
        super("Hangman");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(300, 300, 620, 300);

        //create the menu.///////////add all action listeners as abstract classes within this section//////////
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        JMenu menuHangman = new JMenu("Hangman");
        menuBar.add(menuHangman);

        JMenu menuDifficulty = new JMenu("Difficulty");
        menuHangman.add(menuDifficulty);

        DifficultyHandler diffHandle = new DifficultyHandler();

        rbEasy = new JRadioButtonMenuItem("Easy");
        difficultyGroup.add(rbEasy);
        menuDifficulty.add(rbEasy);
        rbEasy.addActionListener(diffHandle);

        rbMedium = new JRadioButtonMenuItem("Medium");
        difficultyGroup.add(rbMedium);
        menuDifficulty.add(rbMedium);
        rbMedium.setSelected(true);
        rbMedium.addActionListener(diffHandle);

        rbHard = new JRadioButtonMenuItem("Hard");
        difficultyGroup.add(rbHard);
        menuDifficulty.add(rbHard);
        rbHard.addActionListener(diffHandle);

        rbNoMercy = new JRadioButtonMenuItem("No Mercy");
        difficultyGroup.add(rbNoMercy);
        menuDifficulty.add(rbNoMercy);
        rbNoMercy.addActionListener(diffHandle);

        JMenuItem menuItemChangeList = new JMenuItem("Change List");
        menuHangman.add(menuItemChangeList);
        menuItemChangeList.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                frame.setVisible(true);
            }
        });

        JMenuItem menuItemNewWord = new JMenuItem("New Word");
        menuHangman.add(menuItemNewWord);
        menuItemNewWord.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                int option = JOptionPane.showConfirmDialog(null, "This will give you a loss. Are you sure?", "Quitter!", JOptionPane.OK_CANCEL_OPTION);
                if(option == JOptionPane.OK_OPTION){
                    resetGame();
                    newWord();
                    losses++;
                    lossesLabel.setText("Losses: " + losses);
                }
            }
        });

        JMenuItem menuItemResetStats = new JMenuItem("Reset Stats");
        menuHangman.add(menuItemResetStats);
        menuItemResetStats.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                int option = JOptionPane.showConfirmDialog(null, "Are you sure you want to reset your stats?\n" +
                        "(this will not reset the current word)", "Reset Stats?", JOptionPane.OK_CANCEL_OPTION);
                if(option == JOptionPane.OK_OPTION){
                    wins = 0;
                    losses = 0;
                    winsLabel.setText("Wins: " + wins);
                    lossesLabel.setText("Losses: " + losses);
                }
            }
        });

        JMenuItem menuItemExit = new JMenuItem("Exit");
        menuHangman.add(menuItemExit);
        //end menu creation./////////////////

        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);  //use absolute positioning for all frames.

        // initialize the panel of alphabetical buttons
        int row = 176, col = 10;
        for (Character letter = 'a'; letter <= 'z'; letter++) {
            JButton b = new JButton(letter.toString());
            b.setBounds(col, row, 45, 23);
            contentPane.add(b);
            letterButtons.put(letter, b);           
            if (letter == 'm') {
                row = 204;
                col = 10;
            }
            else {
                col += 45;
            }
        } 


        //display remaining attempts. Decrement each time you miss a letter.
        trysRemaining = new JLabel("Trys Remaining:   " + trys);
        trysRemaining.setBounds(385, 70, 150, 20);
        contentPane.add(trysRemaining);
        trysRemaining.setFont(new Font("comic sans ms", Font.BOLD, 14));
        trysRemaining.setForeground(green);

        //display wins and losses. Resets when program ends.
        winsLabel.setBounds(385, 10, 75, 20);
        contentPane.add(winsLabel);
        winsLabel.setFont(new Font("comic sans ms", Font.BOLD, 14));
        lossesLabel.setBounds(485, 10, 75, 20);
        contentPane.add(lossesLabel);
        lossesLabel.setFont(new Font("comic sans ms", Font.BOLD, 14));

        currentDifficulty.setBounds(385, 40, 200, 20);
        contentPane.add(currentDifficulty);
        currentDifficulty.setFont(new Font("comic sans ms", Font.BOLD, 14));


        //initialize 30 JLabels (30 is the max length for a word in this version).
        //these will later be added to the frame and each time a word is loaded, the corresponding JLabels will be '_' by default,
        //and when the user guesses a correct letter, all occurrences of that letter will change from '_' to the letter.
        for(int i=0; i<30; ++i){
            letters[i] = new JLabel();
        }
        HandlerClass handler = new HandlerClass();  
        for(JButton b : letterButtons.values()){
            b.addActionListener(handler);
        }   
        try{
            for (int i = 0; i < 11; i++) {
                body[i] = ImageIO.read(new File("src\\Pics\\hang" + i + ".gif"));
            }
            drawings = true;
        }catch(Exception e){
            JOptionPane.showMessageDialog(null, "The picture files are corrupt, so you won't see the drawings");
            drawings = false;
        }

        //create victim label to display victim (if possible)
        victim = new JLabel();
        victim.setBounds(150, 10, 200, 200);
        contentPane.add(victim);
        drawVictim();
    }

    //handler class for the difficulty level option buttons. 
    public class DifficultyHandler implements ActionListener{
        public void actionPerformed(ActionEvent e){

            String action = e.getActionCommand();
            Boolean fresh = true;  //if you have already guessed a letter for the current word, this will be false.
            for(JButton b : letterButtons.values()) {  //(have you guessed anything yet? you can tell by checking for disabled buttons)
                if(!b.isEnabled()) {
                    fresh = false;
                }
            }
            int option = JOptionPane.showConfirmDialog(null, "Are you sure you want to change difficulty?\n" +
                    "If you have already guessed a letter for this word\n" +
                    "then you will get a loss. If you haven't guessed\n" +
                    "yet then you will not get a loss", "Change Difficulty?", JOptionPane.OK_CANCEL_OPTION);
            if(option == JOptionPane.OK_OPTION){ //if they are sure, continue.
                if (action.equals("Easy")) {
                    resetGame();
                    newWord();
                    setTrysHere = 10;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = ActionType.Easy;
                    trysRemaining.setForeground(green);
                }
                else if (action.equals("Medium")) {
                    resetGame();
                    newWord();
                    setTrysHere = 6;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = ActionType.Medium;
                    trysRemaining.setForeground(green);
                }
                else if (action.equals("Hard")) {
                    resetGame();
                    newWord();
                    setTrysHere = 4;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = ActionType.Hard;
                    trysRemaining.setForeground(yellow);
                }
                else {
                    resetGame();
                    newWord();
                    setTrysHere = 1;
                    trys = setTrysHere;
                    trysRemaining.setText("Trys Remaining:   " + trys);
                    lastDifficulty = ActionType.NoMercy;
                    trysRemaining.setForeground(red);
                }

                if(!fresh){  //if they already guessed something, give them a loss.
                    losses++;
                    lossesLabel.setText("Losses: " + losses);
                }
            }else{  //if they hit cancel then you need to set the selected option button back to where it was before the click. This is why i needed the option buttons to be class level variables instead of being declared inside the constructor like the rest of the menu was.
                switch (lastDifficulty){
                case Easy:
                    rbEasy.setSelected(true);
                    break;
                case Medium:
                    rbMedium.setSelected(true);
                    break;
                case Hard:
                    rbHard.setSelected(true);
                    break;
                case NoMercy:
                    rbNoMercy.setSelected(true);
                    break;
                }
            }
        }
    }

    //the setFrame and setHang are how I access the windows for the ListSelector and Hangman instances. Is there a better way to do this or is this ok? 
    public void setFrame(ListSelector e){
        frame = e;
    }


    //used to set the current word list to whatever the user selected in the ListSelector's comboBox.
    public void setList(ArrayList<String> list){
        words = list;
    }

    //does a lot of stuff to set the next word to be guessed.
    public String newWord(){
        //reset the labels after the game is over so you can reset them for the next word.
        if(!firstGame){
            for(int i=0; i<word.length(); ++i){
                letters[i].setText("");
            }
        }

        //set the current list to whatever the user has selected in the ListSelector.
        setList(frame.getWordsList());

        //choose a random number then use the corresponding word in the current word list.
        arr = words.toArray(new String[0]);
        Random rand = new Random();
        int n = rand.nextInt(arr.length);
        word = arr[n].toLowerCase();

        //create a label array with a length of word.length() and show a '_' for each letter until guessed.
        //xPos is used to line them up 10 pixels away from eachother.
        Font labelFont = new Font("courier", Font.BOLD, 20);
        int xPos = 300; 
        for(int i=0; i<word.length(); ++i){
            if(word.charAt(i)!=' '){
                letters[i].setFont(labelFont);  //must use monospaced font otherwise wide letters like 'm' and 'w' will crowd the other letters.
                letters[i].setText("_");
            }else{
                letters[i].setFont(labelFont);
                letters[i].setText(" "); //if there is a space, then that slot is set to " ". Sort of gives it away for the states but it's either that or I combine the words like 'newyork' which looks stupid.
            }
            letters[i].setBounds(xPos, 131, 55, 30);
            contentPane.add(letters[i]);
            xPos += 15;
        }
        firstGame = false;;
        return word;
    }

    public void drawVictim(){
        //if(drawings) is there so that if any of the image files failed to load then the game will just continue without the drawing of the victim.
        //this method checks diffictulty and draws the images based on difficulty && number of bad guesses. They were set up so the drawing was even with number of guesses.
        if(drawings){

            switch (lastDifficulty){
            case Easy:
                victim.setIcon(new ImageIcon(body[badGuesses]));
                break;

            case Medium:
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(body[0]));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(body[1]));
                    break;
                case 2:
                    victim.setIcon(new ImageIcon(body[2]));
                    trysRemaining.setForeground(yellow);
                    break;
                case 3:
                    victim.setIcon(new ImageIcon(body[4]));
                    break;
                case 4:
                    victim.setIcon(new ImageIcon(body[6]));
                    trysRemaining.setForeground(red);
                    break;
                case 5:
                    victim.setIcon(new ImageIcon(body[8]));
                    break;
                case 6:
                    victim.setIcon(new ImageIcon(body[10]));
                    break;
                }
                break;

            case Hard:
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(body[0]));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(body[1]));
                    break;
                case 2:
                    victim.setIcon(new ImageIcon(body[2]));
                    trysRemaining.setForeground(red);
                    break;
                case 3:
                    victim.setIcon(new ImageIcon(body[6]));
                    break;
                case 4:
                    victim.setIcon(new ImageIcon(body[10]));
                    break;
                }
                break;

            case NoMercy:
                switch (badGuesses){
                case 0:
                    victim.setIcon(new ImageIcon(body[0]));
                    break;
                case 1:
                    victim.setIcon(new ImageIcon(body[10]));
                    break;
                }
                break;
            }
        }

    }

    //one handler class for all the letter buttons.
    public class HandlerClass implements ActionListener{
        public void actionPerformed(ActionEvent e){
            Character guess = e.getActionCommand().charAt(0); //set guess to the character of the button pressed.
            Boolean done = true;                            //used later to see if there is a '_' left so I know if word is still not guessed completely.
            JButton source = (JButton)e.getSource();
            source.setEnabled(false);                         //used to disable the button after pressing it.
            //check to see if the word even contains the string, so if it doesn't you don't waste time looping and searching.
            //this if statement isn't needed, but I figured it would save some (surely insignificant) loop time. Do you think so?
            if(word.contains(guess.toString())){
                for(int i=0; i<word.length(); ++i){   //if it does, find each location and change it to the letter guessed.
                    if(word.charAt(i) == guess){
                        letters[i].setText(guess.toString());
                    }
                }
            }else{
                --trys;
                ++badGuesses;
                trysRemaining.setText("Trys Remaining:   " + trys);
                drawVictim();
                if(trys==0){
                    JOptionPane.showMessageDialog(null, "YOU'RE OUT OF CHANCES! \n" +
                            "The word was \"" + word + "\". \n" +
                            "Try to get the next one right!");
                    ++losses;
                    lossesLabel.setText("Losses: " + losses);
                    resetGame();
                    newWord();
                }
            }
            //if any '_' are left then you haven't guessed all the letters yet.
            for(int i=0; i<word.length(); ++i){
                if(letters[i].getText().equals("_")){
                    done = false;
                }
            }
            //if there are no '_' left then the word is guessed. Congratulate user, update stats and reset game.
            if(done==true){
                JOptionPane.showMessageDialog(null, "Congratulations! You guessed the word!\n" +
                        "Keep it going!");
                ++wins;
                winsLabel.setText("Wins: " + wins);
                resetGame();
                newWord();
            }
            done = true;
        }
    }

    //when game is won or lost, this is called to reset everything for next game.
    public void resetGame(){
        for(JButton b : letterButtons.values()){
            b.setEnabled(true);
        }
        trys = setTrysHere;
        trysRemaining.setText("Trys Remaining:   " + trys);
        badGuesses = 0;
        victim.setIcon(new ImageIcon(body[0]));

        //reset the color of the trysRemaining label accordingly.
        switch (lastDifficulty){
        case Easy:
            trysRemaining.setForeground(green);
            break;
        case Medium:
            trysRemaining.setForeground(green);
            break;
        case Hard:
            trysRemaining.setForeground(yellow);
            break;
        case NoMercy:
            trysRemaining.setForeground(red);
            break;
        }

    }
}

This also fixes a number of other minor issues as well.

Edited 3 Years Ago by Schol-R-LEA

Schol-R-Lea:

Note also that you cannot use a String as the index of a swich() statement;

yes, you can. this is one of the new functionalities that were introduced in Java 7.

Re stultusk's post: I agree absolutely except for the anonymous inner class listener. I would always try to use that rather than implementing the listener in the main class because it scales to any number of listeners and avoids the "do everything in one listener" multiple-if nightmare. (And with Java 8 the anonymous inner class pattern will come down to a single line of simple code)
Re Schol-R-LEA's, you can have a switch on a String (java 7 onwards)

well, that is indeed a personal choice. as for Java 8, it'll be less boiler plate code, and probably, I'll have a different opinion the, but for the moment, when the number of components to which to add an actionlistener is pretty big, I prefer one 'bigger' implementation over each time declaring and initializing a new ActionListener.

Wow, I am overwhelmed with how much advice everyone is giving me. Thank you so much! Really, thank you for taking the time to look through all that. This is the first time anyone has looked at my code (other than my wife "Honey look at this! Look what I made" with the response ".....coooool....." hahah)

/
Rubberman: Thank you for the response and for the book recommendations. I will definitely get both of those book and probably start them after I'm finished with the one I'm on now. It's a Wrox publication written by Ivor Horton I beleive (don't have the book with me now) but its a great book and he makes things very simple to understand. I am glad to hear that I am coming along ok and I will continue studying!

/
stultuske: Thank you also for your very detailed reply, I did read through the entire reply about 4 times to soak it all up. And yes, everything is VERY simple in my code, no complicated concepts. It is like building a house but you only have 1 type of brick for everything including the roof. So you do wierd things and way more work than if you just had a little wood on hand ;) I haven't learned all the tools I need to be efficient yet.
It is great to see how much shorter this code could all be. I will be going through all the code soon and applying all these tricks. I like to do it over and over too, so I basically apply everything and get it to work, then just don't save my work, and start all over fixing it up again so it gets into my head firmly. One thing I also had no idea about was the different type of ActionListener that you did. I never knew you could do that! I honestly haven't gotten to the Action Listener part of the book, it's a few chapters away, but when I was making this game I realized I needed the Action Listener so I watched a few quick video tutorials on youtube to get the idea and they used the other style. One question though; if I do it your way, how would I have multiple handlers? Or would that mean that all the handlers need to be one class? Because the reason that I specifically split that handler away from other ones (such as difficulty handler) was because the fact that all the names of the letter buttons are in fact just a single letter, it made it so I didnt need any if statements. I would have needed 26 if statements, but instead I just took the name (caption) on that button and assigned it to a char variable, then checked it against the word being guessed. Is that an ok way to do it in that case?
Also, with the repeating code, you are absolutely right, I do have a lot of blocks that could be lumped into a method and I will be doing that now. As a funny side note, at one point, the entire game resetting was not in it's own method. But after I realized I had a lot of different situations that needed to reset the game, I threw it into a separate method. I suppose there were many things that I needed to do the same with.
I have a question with the code you shortened for me regarding "whenToYellow" and all that. I see what you are saying but I am slightly confused, because the entire point of that section was the 'victim.setIcon'. This was all there really to draw the victim differently based on what difficulty you were on because with a harder difficulty the victim is drawn in less trys. The whole color of the 'trysRemaining' was an after thought towards the end of the process. I thought "I should change the display to yellow and red when they are almost out of turns" so I just threw that one line into each difficulty switch to do it while it was drawing that icon. The shortened code you gave takes care of the color of the JLabel with the trys remaining, but doesnt do the main point which was drawing the victim one body part at a time. Or I am just missing it, which is highly likely actually lol.

/
Schol-R-LEA: Thank you also for your advice, I appreciate it. I actually haven't learned Hashmaps yet, but I do believe they are in the next chapter I am about to read on Collections Framework. I have seen hashmaps creeping up in a lot of code that I read online and I see that they are very common and useful so I am quite excited to learn what they can do. Also, I love the advice about not separately declaring each button. I didn't even catch that when writing. I used that elsewhere I believe but didnt even think to do it right there. And as for the switch statements, you can definitely use the string. I used them all over the place and it works ok. I never would have known that it was a new feature though! I learned a lot of my stuff also from the oracle learning paths. They are not a bad way to learn but they also can leave out things and assume that I know what they are talking about. Also, I could use a little practice with enums :}, haven't done much with them but they seem powerful.
Thank you very much for your detailed response.

/
JamesCherrill: Thank you for your post also. I sort of agree with you on the anonymous listener and having multiple handlers. The reason I say this is because back in March when I first dove into this programming endeavor, I 'attempted' to make a calculator just like windows. (had a gui and everything but a TERRIBLE layout). I ended up having over 60 if statements and my handler class was over 400 lines long and insanely confusing. Afterwards I realized how disgusting my code was. It was still fun, but the calc had so many glitches (not to mention that in my lack of knowledge, I built the ENTIRE thing with int instead of doubles... and didnt realize it till about 20 hours later I did 3/2... Then said.. oh shit!) Very embarassing lol. Thank you also for your reply.

/
Thank you so much everyone for your input. Believe me, I take recommendations very much into consideration and will actually probably spend the next week just adjusting this code and trying out every single thing you guys said. I will keep the code I wrote in a separate file so 20 years I can look back and see how terrible I was when I started haha.

By the way, if anyone wants to try the thing out to see how it all came together, I crammed it all into an exe installer just so my friends could try it. I know that isn't really what you normally do with Java, but that's what happens when you're a newbie.. you do wierd newbie things like that lol. I have 65 more word lists that I will be adding soon too.
http://www.filedropper.com/hangmansetupandcode

This article has been dead for over six months. Start a new discussion instead.