Package name: Proj02 File Name: SnakeGame.java SnakeBoard.java Position.java Inc
ID: 3604345 • Letter: P
Question
Package name: Proj02
File Name:
SnakeGame.java
SnakeBoard.java
Position.java
Include comments
SnakeGame is a text based snake game. The snake moves around the board until it runs into itself or goes out of bounds. I realize that Snake usually has more bells and whistles, but we’ll stick to a basic version for this project.
• The edge of the board is drawn with #’s
• The snake is drawn with S’s.
• The snake has 20 spaces horizontally and 10 spaces vertically where it can move.
o The edge of the board is outside the 20 x 10 area
• Initially the snake is 2 segments long – at (1, 1) and (1, 2).
o Let the positions be 0-based. (0, 0) is the upper left.
o X grows positive to the right and y grows positive going down.
o The initial board, with the 2 initial snake segments, looks like:
######################
# #
# S #
# S #
# #
# #
# #
# #
# #
# #
# #
######################
• If the snake moves to a spot already occupied by the snake or into the edge of the board, then the game is over.
o Keep track of the number of moves before the game ends.
How to play the game (this is basically main()):
• Initialize the board and snake
• Initialize the number of moves made
• Print the board
• Continue until the game ends (snake runs into edge or itself)
o Remove the oldest segment from the snake
For example, the first time, (1, 1) will be removed from the snake
o Ask the user for a move (l for left, r for right, u for up, and d for down)
o Move the snake twice in that direction
For example, if the user chooses ‘r’ and the last segment added to the snake was (1, 2), then add (2, 2) and (3, 2) to the snake
• Right increases the x-coordinate. So (1, 2) moves to (2, 2) and (2, 2) moves to (3, 2)
I had a method in my SnakeBoard class that did a single move and called it twice. I felt this option would be more flexible if I wanted to change the game in the future.
o Print the board
o Increment the number of moves made
• After the game is over, print the number of moves made
Your program should have 3 classes/files:
• SnakeGame class
o Contains main() – essentially the code shown above in how to play the game
• SnakeBoard class
o Need some final instance variables
Width of the board
Height of the board
o 2 instance variables
Array or ArrayList of Position objects – will contain all of the Positions of the Snake
• I used ArrayList, but you may use a regular array if you prefer
Flag to remember if snake has made an illegal move (moved onto itself or off the edge of the board)
• This instance variable is optional as it depends on how you design the solution
• If you use it, the game is over when this flag becomes true
o Constructor
Initialize the Array/ArrayList
Add first 2 snake segments to the Array/ArrayList
• You should create a new Position object, set its x, y, add it to your Array/ArrayList
• Then you should create a second ‘new’ Position, set its x, y, and add it to your Array/ArrayList
• Do NOT try to reuse the same Position object – you’ll want to call new a second time
o print method to print the board
This method will need nested loops to print the two-dimensional board
Within the loops, I checked to see if the x,y location that I was about to print was a Position in my Array/ArrayList. If it was in there, then I printed ‘S’, otherwise I printed ‘ ‘ (space).
• This is where I used the snakeHere method listed below
o removeSnake method to remove the oldest Position in the Array/ArrayList
o moveSnake method to add one more Position to the Array/ArrayList
pass in a direction to move
use the last Position of the Snake and the direction to calculate the x, y of the new Position
add the new Position to your Array/ArrayList if it is a valid move
• This is where I used the validMove method listed below
o gameOver method that returns true if the game is over, false otherwise
o validMove method that returns true if moving to x, y is okay, false otherwise
I also used the snakeHere method to help decide if this is a valid move or not
o snakeHere method that returns true if the x,y you are checking is an x,y currently in your Array/ArrayList
• Position class
o Simple class that contains x, y and matching get/set methods
o The following is an example of a Position class – feel free to use it, but please add some comments!
package proj02;
public class Position
{
private int x;
private int y;
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
}
Requirements:
• Put System.out.println() after each call to next(). This will help you match the Test Program and make it easier to use it.
Sample Run #1: (the highlighted text is what the user types)
######################
# #
# S #
# S #
# #
# #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? u
######################
# S #
# S #
# S #
# #
# #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? u
######################
# S #
# S #
# #
# #
# #
# #
# #
# #
# #
# #
######################
Game over, you moved 2 times
Sample Run #2: (the highlighted text is what the user types)
######################
# #
# S #
# S #
# #
# #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? r
######################
# #
# #
# SSS #
# #
# #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? r
######################
# #
# #
# SSSS #
# #
# #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? r
######################
# #
# #
# SSSSS #
# #
# #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? r
######################
# #
# #
# SSSSSS #
# #
# #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? d
######################
# #
# #
# SSSSS #
# S #
# S #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? l
######################
# #
# #
# SSSS #
# S #
# SSS #
# #
# #
# #
# #
# #
######################
Move (l/r/u/d)? u
######################
# #
# #
# SSS #
# S S #
# SSS #
# #
# #
# #
# #
# #
######################
Game over, you moved 7 times
Explanation / Answer
package com.blogspot.geekonjava;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Board extends JPanel implements ActionListener {
private final int B_WIDTH = 300;
private final int B_HEIGHT = 300;
private final int DOT_SIZE = 10;
private final int ALL_DOTS = 900;
private final int RAND_POS = 29;
private final int DELAY = 140;
private final int x[] = new int[ALL_DOTS];
private final int y[] = new int[ALL_DOTS];
private int dots;
private int apple_x;
private int apple_y;
private boolean leftDirection = false;
private boolean rightDirection = true;
private boolean upDirection = false;
private boolean downDirection = false;
private boolean inGame = true;
private Timer timer;
private Image ball;
private Image apple;
private Image head;
public Board() {
addKeyListener(new TAdapter());
setBackground(Color.black);
setFocusable(true);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
loadImages();
initGame();
}
private void loadImages() {
ImageIcon iid = new ImageIcon("dot.png");
ball = iid.getImage();
ImageIcon iia = new ImageIcon("apple.png");
apple = iia.getImage();
ImageIcon iih = new ImageIcon("head.png");
head = iih.getImage();
}
private void initGame() {
dots = 3;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z * 10;
y[z] = 50;
}
locateApple();
timer = new Timer(DELAY, this);
timer.start();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
if (inGame) {
g.drawImage(apple, apple_x, apple_y, this);
for (int z = 0; z < dots; z++) {
if (z == 0) {
g.drawImage(head, x[z], y[z], this);
} else {
g.drawImage(ball, x[z], y[z], this);
}
}
Toolkit.getDefaultToolkit().sync();
} else {
gameOver(g);
}
}
private void gameOver(Graphics g) {
String msg = "Game Over";
Font small = new Font("Helvetica", Font.BOLD, 14);
FontMetrics metr = getFontMetrics(small);
g.setColor(Color.white);
g.setFont(small);
g.drawString(msg, (B_WIDTH - metr.stringWidth(msg)) / 2, B_HEIGHT / 2);
}
private void checkApple() {
if ((x[0] == apple_x) && (y[0] == apple_y)) {
dots++;
locateApple();
}
}
private void move() {
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
if (leftDirection) {
x[0] -= DOT_SIZE;
}
if (rightDirection) {
x[0] += DOT_SIZE;
}
if (upDirection) {
y[0] -= DOT_SIZE;
}
if (downDirection) {
y[0] += DOT_SIZE;
}
}
private void checkCollision() {
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
if (y[0] >= B_HEIGHT) {
inGame = false;
}
if (y[0] < 0) {
inGame = false;
}
if (x[0] >= B_WIDTH) {
inGame = false;
}
if (x[0] < 0) {
inGame = false;
}
if(!inGame) {
timer.stop();
}
}
private void locateApple() {
int r = (int) (Math.random() * RAND_POS);
apple_x = ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
apple_y = ((r * DOT_SIZE));
}
@Override
public void actionPerformed(ActionEvent e) {
if (inGame) {
checkApple();
checkCollision();
move();
}
repaint();
}
private class TAdapter extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) {
leftDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) {
rightDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_UP) && (!downDirection)) {
upDirection = true;
rightDirection = false;
leftDirection = false;
}
if ((key == KeyEvent.VK_DOWN) && (!upDirection)) {
downDirection = true;
rightDirection = false;
leftDirection = false;
}
}
}
}
First we will define the constants used in our game.
private final int B_WIDTH = 300;
private final int B_HEIGHT = 300;
private final int DOT_SIZE = 10;
private final int ALL_DOTS = 900;
private final int RAND_POS = 29;
private final int DELAY = 140;
The B_WIDTH and B_HEIGHT constants determine the size of the board. The DOT_SIZE is the size of the apple and the dot of the snake. The ALL_DOTS constant defines the maximum number of possible dots on the board (900 = (300*300)/(10*10)). The RAND_POS constant is used to calculate a random position for an apple. The DELAY constant determines the speed of the game.
private final int x[] = new int[ALL_DOTS];
private final int y[] = new int[ALL_DOTS];
These two arrays store the x and y coordinates of all joints of a snake.
private void loadImages() {
ImageIcon iid = new ImageIcon("dot.png");
ball = iid.getImage();
ImageIcon iia = new ImageIcon("apple.png");
apple = iia.getImage();
ImageIcon iih = new ImageIcon("head.png");
head = iih.getImage();
}
In the loadImages() method we get the images for the game. The ImageIcon class is used for displaying PNG images.
private void initGame() {
dots = 3;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z * 10;
y[z] = 50;
}
locateApple();
timer = new Timer(DELAY, this);
timer.start();
}
In the initGame() method we create the snake, randomly locate an apple on the board, and start the timer.
private void checkApple() {
if ((x[0] == apple_x) && (y[0] == apple_y)) {
dots++;
locateApple();
}
}
If the apple collides with the head, we increase the number of joints of the snake. We call the locateApple() method which randomly positions a new apple object.
In the move() method we have the key algorithm of the game. To understand it, look at how the snake is moving. We control the head of the snake. We can change its direction with the cursor keys. The rest of the joints move one position up the chain. The second joint moves where the first was, the third joint where the second was etc.
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
This code moves the joints up the chain.
if (leftDirection) {
x[0] -= DOT_SIZE;
}
This line moves the head to the left.
In the checkCollision() method, we determine if the snake has hit itself or one of the walls.
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
If the snake hits one of its joints with its head the game is over.
if (y[0] >= B_HEIGHT) {
inGame = false;
}
The game is finished if the snake hits the bottom of the board.
Snake.java
package com.blogspot.geekonjava;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Snake extends JFrame {
public Snake() {
add(new Board());
setResizable(false);
pack();
setTitle("Snake");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame ex = new Snake();
ex.setVisible(true);
}
});
}
}
This is the main class.
setResizable(false);
pack();
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.