I have been looking over past exam papers for revision for an upcoming uni exam for Java and one of the questions was, asking for code to add to a given abacus program to make the grid display 3 counters in each column of a 10x6. e.g. as soon as the JPanel opens.

So far the current program enables the user to click in each column and add counters to each column, so you can do this manually but it is not the way to do it as when its run there are no counters present in the abacus grid. I am unsure as to how i can complete this correctly although i am sure it would likely be a for loop?? I know nothing about the program itself or even what everything does??

The program is in 3 classes: Test2Model, Test2Frame and Test2Panel but the panel class is the one i believe i need to add code to? Please can anybody help me understand this? This is merely revision but would be hugely helpful for me. Thanks, the Test2Panel class is included below....

class Test2Panel extends JPanel implements MouseListener
{
    Test2Model model;

    public Test2Panel()
    {
        addMouseListener(this);
        int test_num_pegs = 10;
        int test_max_counters = 6;
        model = new Test2Model(test_num_pegs, test_max_counters);

    }



    Rectangle getRect(int i,int j)
    {
        if(i<0 || j<0 || i>=model.getNumPegs() || j >=model.getMaxCounters())
            return null;

        int w = getWidth()/model.getNumPegs();
        int h = getHeight()/model.getMaxCounters();

        System.out.println("getRect: w is " + w + " and h is " + h);

        int x = i*w;
        int y = j*h;

        Rectangle r = new Rectangle(x,y,w,h);
        return r;
    }
    int getColumn(int x)
    {
        return x*model.getNumPegs()/getWidth();
    }


    public void mouseReleased(MouseEvent event)
    {
    }
    public void mousePressed(MouseEvent event)
    {
    }
    public void mouseClicked(MouseEvent event)
    {
        int b = event.getButton();
        int col = getColumn(event.getX());

        if(b==1)
        {
            model.addCounter(col);
        }
        if (b==3)
        {
            model.removeCounter(col);
        }
        repaint();
    }
    public void mouseEntered(MouseEvent event)
    {
    }
    public void mouseExited(MouseEvent event)
    {
    }



    public void paint(Graphics g)
    {
        g.setColor(Color.white);
        int w = getWidth();
        int h = getHeight();
        g.fillRect(0,0,w,h);


        Graphics2D g2 = (Graphics2D)g;

        g2.setColor(Color.black);
        for(int j = 0;j<model.getMaxCounters();j++)
        {
            for(int i = 0;i<model.getNumPegs();i++)
            {
                Rectangle r = getRect(i,j);
                g.drawRect(r.x,r.y,r.width, r.height);
            }
        }

        int max_counters = model.getMaxCounters();
        for(int i = 0;i<model.getNumPegs();i++)
        {
            int num_pegs = model.getNumCounters(i);
            for(int j = 0;j<num_pegs;j++)
            {
                Rectangle r = getRect(i,max_counters-j-1);
                g2.setColor(Color.red);
                g2.fillOval(r.x,r.y,r.width,r.height);
                g2.setColor(Color.black);
                g2.drawOval(r.x,r.y,r.width,r.height);
            }

        }


    }


    // copied from the W2MouseEvents for convenience
    // (so we can run the program from Test2Panel class too!)
    public static void main(String[] args)
    {
        Test2Frame w = new Test2Frame();
        w.setVisible(true);
    }


}

Dani AI

Generated

This thread goal is to show three counters in every column as soon as the JPanel opens. The click handler already calls model.addCounter(col), so pre-filling the model at startup is the simplest, most robust approach. The common pitfall that caused the original confusion is passing a single column value (or a pixel X) repeatedly; the initializer must pass each peg index in turn.

A compact, safe initializer for Test2Panel (run on the EDT and with bounds checking) is shown below:

private void initCounters(int perPeg) {
    int pegs = model.getNumPegs();
    int max = model.getMaxCounters();
    int toAdd = Math.min(perPeg, max);
    for (int peg = 0; peg < pegs; peg++) {
        for (int k = 0; k < toAdd; k++) {
            model.addCounter(peg);   // pass the peg index, not pixel X
        }
    }
    repaint();
}

// call from the panel constructor after model is created:
SwingUtilities.invokeLater(() -> initCounters(3));

For cleaner separation of concerns, add the same logic inside Test2Model as a method such as fillAllPegs(int perPeg). That keeps UI code out of the model and makes tests easier: model.fillAllPegs(3);.

Troubleshooting notes and improvements: confirm addCounter takes a peg index (not pixels); ensure perPeg <= model.getMaxCounters(); call repaint() on the EDT after changes; avoid heavy prints inside paint routines; and prefer overriding paintComponent(Graphics g) with a super.paintComponent(g) call (instead of overriding paint) for correct Swing behavior. This implements ’s nested-loop idea and clarifies the confusion raised by about adding three counters only to a single column.

Recommended Answers

All 4 Replies

If you look at the mouseClicked method you will see how that adds a counter - there's a simple method it calls. You jiust need to call that same method 3 times for each column in your initialisation.

I was thinking that maybe there would be some way to create a method in the Model class called e.g. "addCountersToAllPegs()" then call this 3 times from the constructor of the panel class?? Although i understand that would work, I am clueless as to how to set that up??

But as you were saying i can indeed see how the addCounter method works given the X position, which then determines which column to put counters in. But calling this 3 times would only add 3 counters to one column right?

This is the expected outcome to be shown once the program is initialised. Each of the columns were clicked three times to get this image but, on a normal start up the whole grid would be empty.

9c97b9b1704a18928ea81c6efabcf81c

Yes, you could put that into a method, eg addCountersToAllPegs() - maybe addCountersToAllPegs(int numberOfCountersPerPeg) would be even cleaner becuase you can call that just once and pass in the number of pegs.
You know how many pegs there are, so you can just have a simple nested loop

// pseudo code
for peg = 0 to model.getNumPegs()-1
  for n = 1 to numberOfCountersPerPeg
     model.addCounter(peg);
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.