There are many (many, many) degrees of freedom for answering this question. In fact, one could consider it as "too broad", as it is not much more specific than "how can I paint something in Swing?" (with "something" being a snake or a ladder here). There is a reason of why a significant part of game development is not only plain programming, but also the graphics design etc.
It is not clear how much the task of your course is focussed on exactly this point. If it is a general computer science course, then there likely is no need to spend dozens of hours for making the "prettiest" game. Instead, it could be sufficient to draw plain lines between the fields that the snakes/ladders should connect. Green lines for snakes, brown lines for ladders. However, maybe the priorities are different.
Regarding this question in particular, there are, broadly speaking, two options:
- Paint the snakes as images
- Paint the snakes as graphical objects
MadProgrammer showed in his answer the approach of using images. They can be rotated and drawn and scaled arbitrarily. In fact, when you have an image, say of size 100x1000, then you could make it span two arbitrary points. So if you have the points (200,400) and (700,1100) on the screen, then you can compute an orientation and scaling for the image so that the top center point of your image is located at (200,400), and the bottom center point is at (700,1100) - which is likely a requirement that could appear when you want to "draw a ladder starting at one field and ending at another".
The issue that I saw regarding the snakes was that the "contents" of the image would have to depend on the start- and end point. Namely, a snake that is painted between two fields that are close to each other might have a completely different shape than one that is painted between two distant fields.
(Similarly, a ladder: The number of steps that the ladder should have would certainly depend on the distance between the fields that it connects).
So, I did some "recreational programming" here, and created a snake painting class. The difference, compared to images, is that the snakes are graphical objects - particularly, they are composed of Shape
objects. The tricky part is the body of the snake: It should have some waves, and a certain thickness, and the thickness should largely be constant along the body, except for the tail part....
Again: There are many degrees of freedom, and of course, this is just a snippet, quickly written down, to see (mainly for myself) of how one could tackle this problem of "drawing a snake body".
The result is a snake where you can drag around the head and tail between arbitrary points:

Some of the degrees of freedom that I mentioned are summarized as (compile-time) variables in the Snake
class. One could, for example, adjust the number of "waves" based on the distance between the head and the tail point:

But these are things that I'll leave to the real artists ;-)
The code is a bit crude and largely uncommented, but maybe someone finds it helpful nevertheless:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SnakeDrawing
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new SnakeDrawingPanel());
f.setSize(800, 800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class Snake
{
private Point2D point0 = new Point2D.Double(100,500);
private Point2D point1 = new Point2D.Double(700,500);
double bodyWidth = 10;
int waves = 4;
double waveHeight = 0.05;
double tailStart = 0.8;
double headLength = 20;
double headWidth = 16;
double eyeRadius = 6;
double irisRadius = 3;
private Shape body;
private Shape head;
private Shape eyeR;
private Shape eyeL;
private Shape irisR;
private Shape irisL;
void setPoints(Point2D point0, Point2D point1)
{
this.point0.setLocation(point0);
this.point1.setLocation(point1);
AffineTransform at = AffineTransform.getRotateInstance(
currentAngleRad(), point0.getX(), point0.getY());
at.translate(point0.getX(), point0.getY());
createBody(at);
createHead(at);
}
void draw(Graphics2D g)
{
g.setColor(new Color(0,128,0));
g.fill(body);
g.fill(head);
g.setColor(Color.WHITE);
g.fill(eyeR);
g.fill(eyeL);
g.setColor(Color.BLACK);
g.fill(irisR);
g.fill(irisL);
}
private void createBody(AffineTransform at)
{
double distance = point1.distance(point0);
int steps = 100;
Path2D body = new Path2D.Double();
Point2D previousPoint = null;
for (int i=0; i<steps; i++)
{
double alpha = (double)i/(steps-1);
Point2D point = computeCenterPoint(alpha, distance);
if (previousPoint != null)
{
Point2D bodyPoint =
computeBodyPoint(alpha, point, previousPoint);
if (i==1)
{
body.moveTo(bodyPoint.getX(), bodyPoint.getY());
}
else
{
body.lineTo(bodyPoint.getX(), bodyPoint.getY());
}
}
previousPoint = point;
}
previousPoint = null;
for (int i=steps-1; i>=0; i--)
{
double alpha = (double)i/(steps-1);
Point2D point = computeCenterPoint(alpha, distance);
if (previousPoint != null)
{
Point2D bodyPoint =
computeBodyPoint(alpha, point, previousPoint);
body.lineTo(bodyPoint.getX(), bodyPoint.getY());
}
previousPoint = point;
}
this.body = at.createTransformedShape(body);
}
private Point2D computeBodyPoint(
double alpha, Point2D point, Point2D previousPoint)
{
double dx = point.getX() - previousPoint.getX();
double dy = point.getY() - previousPoint.getY();
double rdx = -dy;
double rdy = dx;
double d = Math.hypot(dx, dy);
double localBodyWidth = bodyWidth;
if (alpha > tailStart)
{
localBodyWidth *= (1 - (alpha - tailStart) / (1.0 - tailStart));
}
double px = point.getX() + rdx * (1.0 / d) * localBodyWidth;
double py = point.getY() + rdy * (1.0 / d) * localBodyWidth;
return new Point2D.Double(px, py);
}
private Point2D computeCenterPoint(
double alpha, double distance)
{
double r = alpha * Math.PI * 2 * waves;
double verticalScaling = 1 - (alpha * 2 - 1) * (alpha * 2 - 1);
double y = Math.sin(r) * distance * waveHeight * verticalScaling;
double x = alpha * distance;
return new Point2D.Double(x,y);
}
private void createHead(AffineTransform at)
{
Shape head = new Ellipse2D.Double(
-headLength, -headWidth,
headLength + headLength,
headWidth + headWidth);
this.head = at.createTransformedShape(head);
Shape eyeR = new Ellipse2D.Double(
-headLength * 0.5 - eyeRadius,
-headWidth * 0.6 - eyeRadius,
eyeRadius + eyeRadius,
eyeRadius + eyeRadius);
Shape eyeL = new Ellipse2D.Double(
-headLength * 0.5 - eyeRadius,
headWidth * 0.6 - eyeRadius,
eyeRadius + eyeRadius,
eyeRadius + eyeRadius);
this.eyeR = at.createTransformedShape(eyeR);
this.eyeL = at.createTransformedShape(eyeL);
Shape irisR = new Ellipse2D.Double(
-headLength * 0.4 - eyeRadius,
-headWidth * 0.6 - irisRadius,
irisRadius + irisRadius,
irisRadius + irisRadius);
Shape irisL = new Ellipse2D.Double(
-headLength * 0.4 - eyeRadius,
headWidth * 0.6 - irisRadius,
irisRadius + irisRadius,
irisRadius + irisRadius);
this.irisR = at.createTransformedShape(irisR);
this.irisL = at.createTransformedShape(irisL);
}
private double currentAngleRad()
{
double dx = point1.getX() - point0.getX();
double dy = point1.getY() - point0.getY();
double angleRad = Math.atan2(dy, dx);
return angleRad;
}
}
class SnakeDrawingPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D point0 = new Point2D.Double(100,500);
private Point2D point1 = new Point2D.Double(700,500);
private Point2D draggedPoint = null;
private Snake snake = new Snake();
SnakeDrawingPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
snake.setPoints(point0, point1);
snake.draw(g);
}
@Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
@Override
public void mouseMoved(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseClicked(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mousePressed(MouseEvent e)
{
draggedPoint = null;
double thresholdSquared = 10*10;
if (e.getPoint().distanceSq(point0) < thresholdSquared)
{
draggedPoint = point0;
}
if (e.getPoint().distanceSq(point1) < thresholdSquared)
{
draggedPoint = point1;
}
}
@Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
@Override
public void mouseEntered(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseExited(MouseEvent e)
{
// Nothing to do here
}
}
EDIT:
As an example / extension of the answer by MadProgrammer, here is a program that contains a method that allows you to draw an image between two given points. So, for a given ladder image, you can basically drag around the top- and bottom center point of the image:

Coincidentally, the relevant method is called drawImageBetweenPoints
:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LadderDrawing
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new LadderDrawingPanel());
f.setSize(800, 800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class LadderDrawingPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D point0 = new Point2D.Double(300,300);
private Point2D point1 = new Point2D.Double(500,700);
private Point2D draggedPoint = null;
private BufferedImage ladderImage;
LadderDrawingPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
try
{
ladderImage = ImageIO.read(new File("ladder.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
paintDot(g, point0, 8);
paintDot(g, point1, 8);
drawImageBetweenPoints(g, ladderImage, point0, point1);
}
private static void paintDot(Graphics2D g, Point2D p, double radius)
{
g.fill(new Ellipse2D.Double(
p.getX() - radius, p.getY() - radius,
radius + radius, radius + radius));
}
private static void drawImageBetweenPoints(
Graphics2D g, BufferedImage image, Point2D p0, Point2D p1)
{
AffineTransform at = new AffineTransform();
at.concatenate(AffineTransform.getTranslateInstance(
p0.getX(), p0.getY()));
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double angleRad = Math.atan2(dy, dx) - Math.PI * 0.5;
at.concatenate(AffineTransform.getRotateInstance(angleRad));
double distance = p1.distance(p0);
double scalingY = distance / image.getHeight();
// Default: Uniform scaling
double scalingX = scalingY;
// For keeping the width of the image
//scalingX = 1.0;
// For scaling to a fixed width:
//double desiredWidth = 50;
//scalingX = desiredWidth / image.getWidth();
at.concatenate(AffineTransform.getScaleInstance(scalingX, scalingY));
at.concatenate(AffineTransform.getTranslateInstance(
-image.getWidth() * 0.5, 0));
AffineTransform oldAT = g.getTransform();
g.transform(at);
g.drawImage(image, 0, 0, null);
g.setTransform(oldAT);
}
@Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
@Override
public void mouseMoved(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseClicked(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mousePressed(MouseEvent e)
{
draggedPoint = null;
double thresholdSquared = 10*10;
if (e.getPoint().distanceSq(point0) < thresholdSquared)
{
draggedPoint = point0;
}
if (e.getPoint().distanceSq(point1) < thresholdSquared)
{
draggedPoint = point1;
}
}
@Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
@Override
public void mouseEntered(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseExited(MouseEvent e)
{
// Nothing to do here
}
}
Again, I think that a manual drawing may be more flexible (and, particularly for the ladder, not much more difficult), because you can select the number of steps of the ladder to dynamically adjust based on the distance of the points. For example:


It boils down to a bit of math for computing the positions of the bars and steps, and playing a bit with strokes and shapes:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LadderDrawingManual
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new LadderDrawingManualPanel());
f.setSize(800, 800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class LadderDrawingManualPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D point0 = new Point2D.Double(300,300);
private Point2D point1 = new Point2D.Double(500,700);
private Point2D draggedPoint = null;
LadderDrawingManualPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
paintDot(g, point0, 8);
paintDot(g, point1, 8);
drawLadderBetweenPoints(g, point0, point1);
}
private static void paintDot(Graphics2D g, Point2D p, double radius)
{
g.fill(new Ellipse2D.Double(
p.getX() - radius, p.getY() - radius,
radius + radius, radius + radius));
}
private static void drawLadderBetweenPoints(
Graphics2D g, Point2D p0, Point2D p1)
{
final double ladderWidth = 40;
final double distanceBetweenSteps = 30;
final double barWidth = 5;
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double distance = p1.distance(p0);
double dirX = dx / distance;
double dirY = dy / distance;
double offsetX = dirY * ladderWidth * 0.5;
double offsetY = -dirX * ladderWidth * 0.5;
Line2D lineR = new Line2D.Double(
p0.getX() + offsetX,
p0.getY() + offsetY,
p1.getX() + offsetX,
p1.getY() + offsetY);
Line2D lineL = new Line2D.Double(
p0.getX() - offsetX,
p0.getY() - offsetY,
p1.getX() - offsetX,
p1.getY() - offsetY);
drawBar(g, lineL, barWidth);
drawBar(g, lineR, barWidth);
int numSteps = (int)(distance / distanceBetweenSteps);
for (int i=0; i<numSteps; i++)
{
double stepOffsetX = (i+1) * distanceBetweenSteps;
double stepOffsetY = (i+1) * distanceBetweenSteps;
Line2D step = new Line2D.Double(
p0.getX() + stepOffsetX * dirX - offsetX,
p0.getY() + stepOffsetY * dirY - offsetY,
p0.getX() + stepOffsetX * dirX + offsetX,
p0.getY() + stepOffsetY * dirY + offsetY);
drawBar(g, step, barWidth);
}
}
private static void drawBar(Graphics2D g, Line2D line, double barWidth)
{
Stroke stroke = new BasicStroke(
(float)barWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
Shape bar = stroke.createStrokedShape(line);
g.setColor(new Color(200,100,0));
g.fill(bar);
g.setColor(Color.BLACK);
g.draw(bar);
}
@Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
@Override
public void mouseMoved(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseClicked(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mousePressed(MouseEvent e)
{
draggedPoint = null;
double thresholdSquared = 10*10;
if (e.getPoint().distanceSq(point0) < thresholdSquared)
{
draggedPoint = point0;
}
if (e.getPoint().distanceSq(point1) < thresholdSquared)
{
draggedPoint = point1;
}
}
@Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
@Override
public void mouseEntered(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseExited(MouseEvent e)
{
// Nothing to do here
}
}