蠕虫(蛇)游戏,棋子 Growing/Moving
Worm (Snake) Game, Pieces Growing/Moving
我正在编写一款类似于贪吃蛇的游戏。目前,我正在努力编写我的 move() 和 grow() 方法。这个游戏的运作方式是蠕虫从 1 块开始,每移动 1 块,直到达到 3 块。当它长到3个后,它只能通过吃一个苹果来成长。此游戏中的运动是通过删除第一块并添加位于蠕虫前进方向的新块来完成的。 Move() 应该将蠕虫移动一件,而 grow() 应该将蠕虫一件成长。但是,当调用 grow() 时,蠕虫在调用 move() 之前不会生长。我添加了一个私有变量 "growPiece" 来在调用 grow() 时存储一块,然后在调用 move() 时添加它。但是,我没有得到应有的结果。
public class Worm {
private int x;
private int y;
private Direction direction;
private List<Piece> pieces;
private Piece growPiece;
public Worm(int originalX, int originalY, Direction originalDirection) {
this.x = originalX;
this.y = originalY;
this.direction = originalDirection;
this.pieces = new ArrayList<Piece>();
this.pieces.add(new Piece(this.x, this.y));
this.growPiece = null;
}
public List<Piece> getPieces() {
return this.pieces;
}
public void move() {
if (this.direction == Direction.RIGHT) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX() + 1, this.pieces.get(this.pieces.size() - 1).getY()));
} else if (this.direction == Direction.LEFT) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX() + 1, this.pieces.get(this.pieces.size() - 1).getY()));
} else if (this.direction == Direction.DOWN) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() + 1));
} else if (this.direction == Direction.UP) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() - 1));
}
if (this.growPiece != null) {
this.pieces.add(growPiece);
this.growPiece = null;
}
if (this.pieces.size() > 3 && this.growPiece == null) {
this.pieces.remove(0);
}
}
public void grow() {
if (this.direction == Direction.RIGHT) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX() + 2, this.pieces.get(this.pieces.size() - 1).getY());
} else if (this.direction == Direction.LEFT) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX() - 2, this.pieces.get(this.pieces.size() - 1).getY());
} else if (this.direction == Direction.DOWN) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() + 2);
} else if (this.direction == Direction.UP) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() - 2);
}
}
我无法让方法在不使用参数的情况下相互通信。
在函数move()中,如果this.growPiece在函数开头为null或不为null且pieces.size > 3,grow piece将被添加然后立即删除,因为要么当你到达那个 if 时,growPiece 为 null。
我的意思是,看看 move() 末尾的两个 if 语句。在第一个中,如果 this.growPiece 已分配,则将 growPiece 添加到 ArrayList 中,我必须假设它会产生您想要的效果。添加它后,您使 growPiece 为空。
在接下来的 if 部分条件中,growPiece 为 null,现在它始终为 null。
尝试将其设为 else if,看看效果是否更好。
一切就绪,我使用布尔变量来标记何时增长。
这是赫尔辛基大学 Object 面向编程的 MOOC 蠕虫游戏练习 49 的完整程序代码。
整个代码中的解释可能看起来过于详细,但它们是针对学习者而非专业开发人员的。
该程序的结构与课程练习中提出的结构不完全相同,但结果完全相同。
该程序具有以下逻辑:
- 蠕虫会自动在棋盘上移动,每吃一个苹果它的速度就会加快。
- 玩家可以使用方向键上下左右移动蠕虫。
- 当蠕虫短于三块时,它会随着每次移动自动增长,直到达到三块的长度。
- 如果蠕虫吃了一个苹果,它会在下一步移动时长出一个苹果。
- 如果蠕虫跑进了场地的框架或自己,游戏就结束了。 /如果再添加几行实现计数器的代码,我们就可以确定在发生多少次此类事件后游戏将终止。/
- 该字段的标题,即 JFrame 的标题,用作仪表板显示,显示当前吃掉的苹果数量。并且如果蠕虫跑进了场地的框架或自己,它还会显示有关相应事件的信息。
1.
public enum Direction {
LEFT,
RIGHT,
DOWN,
UP;
}
2.
public class Piece {
private int x;
private int y;
private int width;
private int length;
private int numberOfApples;
public Piece(int x, int y, int width, int length) {
this.x = x;
this.y = y;
this.length = length;
this.width = width;
}
public void setNumberOfApples() {
this.numberOfApples += 1;
}
public int getNumberOfApples() {
return this.numberOfApples;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getWidth() {
return this.width;
}
public int getLength() {
return this.length;
}
/*we use the following two override methods in order to check when the worm runs into itself.
This checking is done through the method "public boolean runsIntoItself(Piece piece)" in class "Worm"*/
@Override
public boolean equals(Object object) {
if (this.getClass() == null) {
return false;
}
if (this.getClass() != object.getClass()) {
return false;
}
Piece compared = (Piece) object;
if (this.x != compared.x || this.y != compared.y
|| this.width != compared.width || this.length != compared.length) {
return false;
}
return true;
}
@Override
public int hashCode() {
if (this.getClass() == null) {
return 1;
}
return this.x + this.y + this.width + this.length;
}
@Override
public String toString() {
return "(" + this.x + ", " + this.y + ", " + this.width + ", " + this.length + ")";
}
}
3.
public class Apple extends Piece {
public Apple(int x, int y, int width, int length) {
super(x, y, width, length);
}
}
4.
import java.util.List;
import java.util.ArrayList;
import javax.swing.JFrame;
public class Worm extends Piece {
private int growth;
Direction direction;
private List<Piece> piecesWorm;
private JFrame frame;
public Worm(int x, int y, int width, int length, Direction originalDirection) {
super(x, y, width, length);
this.growth = 0;
this.direction = originalDirection;
this.piecesWorm = new ArrayList<Piece>();
this.piecesWorm.add(new Piece(x, y, width, length));
frame = new JFrame("Number of Apples: " + super.getNumberOfApples());
}
public JFrame getFrame() {
return this.frame;
}
public Direction getDirection() {
return this.direction;
}
public void setDirection(Direction dir) {
this.direction = dir;
}
public int getLength() {
return this.piecesWorm.size();
}
public List<Piece> getPieces() {
return this.piecesWorm;
}
/*this method takes the calculated new piece from method "calculateNewPiece"
and does the following:
# moves the worm(adds an element to the head and substructs an element from the tail)
# makes the worm grow(only adds an element to the head)
# terminates the game if the worm runs into itself*/
public void move() {
if (this.runsIntoItself(this.calculateNewPiece()) == false) {
if (this.piecesWorm.size() < 3) {
this.piecesWorm.add(this.calculateNewPiece());
} else if (this.piecesWorm.size() >= 3) {
if (this.growth == 0) {
this.piecesWorm.add(this.calculateNewPiece());
this.piecesWorm.remove(0);
} else if (this.growth == 1) {
this.piecesWorm.add(this.calculateNewPiece());
this.growth = 0;
}
}
} else {
this.frame.setTitle("The worm has run into itself!!! "
+ "Total Number of Apples: " + (this.getNumberOfApples()));
try {
Thread.sleep(10000);
} catch (Exception ex) {
}
System.exit(0);
}
}
/*this method calculates the next piece of the worm according to its direction*/
public Piece calculateNewPiece() {
// lpbm stands for "lastPieceBeforeMoving"
Piece lpbm = null;
Piece newPiece = null;
if (this.piecesWorm.size() == 1) {
lpbm = this.piecesWorm.get(0);
} else {
lpbm = this.piecesWorm.get(this.piecesWorm.size() - 1);
}
int x = lpbm.getX();
int y = lpbm.getY();
if (this.direction.equals(Direction.LEFT)) {
newPiece = new Piece(x - 1 - super.getWidth(), y, super.getWidth(), super.getLength());
} else if (this.direction.equals(Direction.RIGHT)) {
newPiece = new Piece(x + 1 + super.getWidth(), y, super.getWidth(), super.getLength());
} else if (this.direction.equals(Direction.DOWN)) {
newPiece = new Piece(x, y + 1 + super.getLength(), super.getWidth(), super.getLength());
} else if (this.direction.equals(Direction.UP)) {
newPiece = new Piece(x, y - 1 - super.getLength(), super.getWidth(), super.getLength());
}
return newPiece;
}
/*this method sets the conditions that determines whether the worm will grow or not when
the method "move()" is called*/
public void grow() {
this.growth = 1;
}
/*this method checks whether the worm runs into an apple*/
public boolean runsInto(Piece piece) {
Piece headPieceOfWorm = this.piecesWorm.get(this.piecesWorm.size() - 1);
if ((headPieceOfWorm.getX() + (headPieceOfWorm.getWidth())) < piece.getX()) {
return false;
} else if (headPieceOfWorm.getX() > (piece.getX() + piece.getWidth())) {
return false;
} else if ((headPieceOfWorm.getX() + (headPieceOfWorm.getWidth())) >= piece.getX()
&& (headPieceOfWorm.getX() <= (piece.getX() + piece.getWidth()))) {
if (headPieceOfWorm.getY() + (headPieceOfWorm.getLength()) < piece.getY()
|| (headPieceOfWorm.getY() > piece.getY() + (piece.getLength()))) {
return false;
}
}
return true;
}
/*this method checks whether the worm runs into itself*/
public boolean runsIntoItself(Piece piece) {
if (this.piecesWorm.contains(piece)) {
return true;
}
return false;
}
@Override
public String toString() {
return "(" + super.getX() + ", " + super.getY() + ", " + super.getWidth()
+ ", " + super.getLength() + ", " + this.getDirection() + ")";
}
}
5.
import java.util.Random;
import java.util.List;
public class AvoidOverlappingPieces {
/*this method calculates the initial position of the apple
so that the apple is within the field and it does not overlap with the worm*/
public Apple initialApple(Worm worm, int fieldWidth, int fieldLength, int pieceWidth, int pieceLength) {
Random random = new Random();
Apple apple = null;
while (true) {
int xApple = random.nextInt(fieldWidth - (pieceWidth));
int yApple = random.nextInt(fieldLength - (pieceLength));
apple = new Apple(xApple, yApple, pieceWidth, pieceLength);
if (xApple + (pieceWidth) < worm.getX()) {
break;
} else if (xApple > worm.getX() + pieceWidth) {
break;
} else if ((xApple + (pieceWidth) >= worm.getX())
&& (xApple <= worm.getX() + pieceWidth)) {
if (yApple + (pieceLength) < worm.getY()
|| (yApple > worm.getY() + (pieceLength))) {
break;
}
}
}
return apple;
}
/*this method calculates the position of the new apple, after the previous one is eaten
by the worm, so that the apple is within the field and it does not overlap with the worm*/
public Apple replaceEatenApple(Worm worm, List<Piece> piecesWorm, int fieldWidth, int fieldLength, int pieceWidth, int pieceLength) {
Random random = new Random();
Apple apple = null;
while (true) {
int xApple = random.nextInt(fieldWidth - (pieceWidth));
int yApple = random.nextInt(fieldLength - (pieceLength));
Apple newApple = new Apple(xApple, yApple, pieceWidth, pieceLength);
if (this.fieldExeptWorm(newApple, piecesWorm) == false) {
apple = newApple;
break;
}
}
return apple;
}
/*this method calculates the available space, taking account of the current
position of worm, and it is used in the method "replaceEatenApple(...)"*/
public boolean fieldExeptWorm(Piece piece, List<Piece> piecesWorm) {
int counter = 0;
for (int i = 0; i < piecesWorm.size(); i++) {
Piece wormPiece = piecesWorm.get(i);
if ((wormPiece.getX() + (wormPiece.getWidth())) < piece.getX()) {
counter++;
} else if (wormPiece.getX() > (piece.getX() + piece.getWidth())) {
counter++;
} else if ((wormPiece.getX() + (wormPiece.getWidth())) >= piece.getX()
&& (wormPiece.getX() <= (piece.getX() + piece.getWidth()))) {
if (wormPiece.getY() + (wormPiece.getLength()) < piece.getY()
|| (wormPiece.getY() > piece.getY() + (piece.getLength()))) {
counter++;
}
}
}
if (counter == piecesWorm.size()) {
return false;
}
return true;
}
}
6.
import java.util.Random;
public class WormGame {
private int fieldWidth;
private int fieldLength;
private int pieceWidth;
private int pieceLength;
private Worm worm;
private Apple apple;
private Random random;
/* class "AvoidOverlappingPieces" contains methods that calculate
the initial spot of the apple and its subsequent positions across the field*/
private AvoidOverlappingPieces properPieces;
protected int applesNumber;
public WormGame(int fieldWidth, int fieldLength) {
this.fieldWidth = fieldWidth;
this.fieldLength = fieldLength;
this.pieceWidth = (this.fieldWidth - 10) / 10;
this.pieceLength = (this.fieldLength - 10) / 10;
this.worm = new Worm(0, 0, pieceWidth, pieceLength, Direction.DOWN);
this.random = new Random();
this.properPieces = new AvoidOverlappingPieces();
this.apple = this.properPieces.initialApple(this.worm, this.fieldWidth, this.fieldLength, this.pieceWidth, this.pieceLength);
}
public int getfieldWidth() {
return this.fieldWidth;
}
public int getfieldLength() {
return this.fieldLength;
}
public Worm getWorm() {
return this.worm;
}
public Apple getApple() {
return this.apple;
}
/*this method accelerates the worm as it grows longer,
through calculating smaller and smaller values for the number
of milliseconds that are fed into "Thread.sleep(int number)",
implemented in the method "action()";*/
public int setDelay(Worm worm) {
int delay = 1500 / worm.getLength();
return delay;
}
/*this method takes the calculated new piece from method "calculateNewPiece"
from class "Worm" and does the following:
# checks whether the new piece of the worm is within the confines of the field.
# if the new piece is within these confines the method "move()" from class "Worm" is called.
# if the worm bupms into an apple, the method "grow()" from class "Worm" and
the method "replaceEatenApple(...)" from class "AvoidOverlappingPieces" are called.
# if the new piece is outside the confines of the field the game is terminated.*/
public void action() {
int x_nextStep = this.worm.calculateNewPiece().getX();
int y_nextStep = this.worm.calculateNewPiece().getY();
if (x_nextStep >= 0 && (x_nextStep + this.pieceWidth) <= this.fieldWidth
&& y_nextStep >= 0 && (y_nextStep + this.pieceLength) <= this.fieldLength) {
try {
Thread.sleep(this.setDelay(this.worm));
} catch (Exception ex) {
}
this.worm.move();
if (this.worm.runsInto(this.apple)) {
this.worm.setNumberOfApples();
this.getWorm().getFrame().setTitle("Number of Apples: " + (this.worm.getNumberOfApples()));
this.worm.grow();
this.apple = this.properPieces.replaceEatenApple(this.worm, this.worm.getPieces(), this.fieldWidth, this.fieldLength, this.pieceWidth, this.pieceLength);
}
} else {
this.getWorm().getFrame().setTitle("The worm has run into a wall!!! "
+ "Total Number of Apples: " + (this.worm.getNumberOfApples()));
try {
Thread.sleep(10000);
} catch (Exception ex) {
}
System.exit(0);
}
}
public int getNumberOfApples() {
return this.applesNumber;
}
}
7.
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
public class KeyboardListener implements KeyListener {
private WormGame wormGame;
public KeyboardListener(WormGame wormGame) {
this.wormGame = wormGame;
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
this.wormGame.getWorm().setDirection(Direction.LEFT);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
this.wormGame.getWorm().setDirection(Direction.RIGHT);
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
this.wormGame.getWorm().setDirection(Direction.DOWN);
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
this.wormGame.getWorm().setDirection(Direction.UP);
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
8.
import java.awt.Color;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.Timer;
public class DrawingBoard extends JPanel implements ActionListener {
private WormGame wormGame;
/*"Timer" sets the speed with which the worm moves.
Since in our case the worm accelerates with every eaten apple through
"Thread.sleep()" applied in method "action()", class "WormGame", the "Timer" here
sets the maximum speed.
In other words:
the initial speed (i.e. delay in the movement of the worm) is the sum of
the value of the integer in "Timer", which is 400, plus the value of the integer in
"Thread.sleep", which is 1500, totalling to 1900.
So, theoretically the value in "Thread.sleep" could be reduced to nearly zero, thus
the maximum speed of 400 milliseconds delay remains the upper speed limit.
Of course these values could be changed as per the desire of the player.*/
Timer tm = new Timer(400, this);
public DrawingBoard(WormGame wormGame) {
this.wormGame = wormGame;
super.setBackground(Color.WHITE);
}
/*this method paints the initial positions of the apple and the worm*/
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (Piece w : this.wormGame.getWorm().getPieces()) {
g.fill3DRect(w.getX(), w.getY(), w.getWidth(), w.getLength(), true);
}
g.setColor(Color.RED);
g.fillOval(this.wormGame.getApple().getX(), this.wormGame.getApple().getY(),
this.wormGame.getApple().getWidth(), this.wormGame.getApple().getLength());
tm.start();
}
/*this method paints the positions of the apple and the worm as they move across the board*/
public void actionPerformed(ActionEvent event) {
this.wormGame.action();
repaint();
}
}
9.
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.WindowConstants;
import javax.swing.JFrame;
public class UserInterface implements Runnable {
private JFrame frame;
private WormGame wormGame;
public UserInterface(WormGame wormGame) {
this.wormGame = wormGame;
}
public void run() {
frame = this.wormGame.getWorm().getFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.createCompoent(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
private void createCompoent(Container container) {
DrawingBoard board = new DrawingBoard(this.wormGame);
container.add(board);
KeyboardListener kL = new KeyboardListener(this.wormGame);
frame.addKeyListener(kL);
container.setPreferredSize(new Dimension(this.wormGame.getfieldWidth(), this.wormGame.getfieldLength()));
}
}
10.
import javax.swing.SwingUtilities;
public class Run {
public static void main(String[] args) {
WormGame game = new WormGame(510, 510);
UserInterface ui = new UserInterface(game);
SwingUtilities.invokeLater(ui);
}
}
我正在编写一款类似于贪吃蛇的游戏。目前,我正在努力编写我的 move() 和 grow() 方法。这个游戏的运作方式是蠕虫从 1 块开始,每移动 1 块,直到达到 3 块。当它长到3个后,它只能通过吃一个苹果来成长。此游戏中的运动是通过删除第一块并添加位于蠕虫前进方向的新块来完成的。 Move() 应该将蠕虫移动一件,而 grow() 应该将蠕虫一件成长。但是,当调用 grow() 时,蠕虫在调用 move() 之前不会生长。我添加了一个私有变量 "growPiece" 来在调用 grow() 时存储一块,然后在调用 move() 时添加它。但是,我没有得到应有的结果。
public class Worm {
private int x;
private int y;
private Direction direction;
private List<Piece> pieces;
private Piece growPiece;
public Worm(int originalX, int originalY, Direction originalDirection) {
this.x = originalX;
this.y = originalY;
this.direction = originalDirection;
this.pieces = new ArrayList<Piece>();
this.pieces.add(new Piece(this.x, this.y));
this.growPiece = null;
}
public List<Piece> getPieces() {
return this.pieces;
}
public void move() {
if (this.direction == Direction.RIGHT) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX() + 1, this.pieces.get(this.pieces.size() - 1).getY()));
} else if (this.direction == Direction.LEFT) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX() + 1, this.pieces.get(this.pieces.size() - 1).getY()));
} else if (this.direction == Direction.DOWN) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() + 1));
} else if (this.direction == Direction.UP) {
this.pieces.add(new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() - 1));
}
if (this.growPiece != null) {
this.pieces.add(growPiece);
this.growPiece = null;
}
if (this.pieces.size() > 3 && this.growPiece == null) {
this.pieces.remove(0);
}
}
public void grow() {
if (this.direction == Direction.RIGHT) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX() + 2, this.pieces.get(this.pieces.size() - 1).getY());
} else if (this.direction == Direction.LEFT) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX() - 2, this.pieces.get(this.pieces.size() - 1).getY());
} else if (this.direction == Direction.DOWN) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() + 2);
} else if (this.direction == Direction.UP) {
this.growPiece = new Piece(this.pieces.get(this.pieces.size() - 1).getX(), this.pieces.get(this.pieces.size() - 1).getY() - 2);
}
}
我无法让方法在不使用参数的情况下相互通信。
在函数move()中,如果this.growPiece在函数开头为null或不为null且pieces.size > 3,grow piece将被添加然后立即删除,因为要么当你到达那个 if 时,growPiece 为 null。
我的意思是,看看 move() 末尾的两个 if 语句。在第一个中,如果 this.growPiece 已分配,则将 growPiece 添加到 ArrayList 中,我必须假设它会产生您想要的效果。添加它后,您使 growPiece 为空。
在接下来的 if 部分条件中,growPiece 为 null,现在它始终为 null。
尝试将其设为 else if,看看效果是否更好。
一切就绪,我使用布尔变量来标记何时增长。
这是赫尔辛基大学 Object 面向编程的 MOOC 蠕虫游戏练习 49 的完整程序代码。 整个代码中的解释可能看起来过于详细,但它们是针对学习者而非专业开发人员的。 该程序的结构与课程练习中提出的结构不完全相同,但结果完全相同。 该程序具有以下逻辑:
- 蠕虫会自动在棋盘上移动,每吃一个苹果它的速度就会加快。
- 玩家可以使用方向键上下左右移动蠕虫。
- 当蠕虫短于三块时,它会随着每次移动自动增长,直到达到三块的长度。
- 如果蠕虫吃了一个苹果,它会在下一步移动时长出一个苹果。
- 如果蠕虫跑进了场地的框架或自己,游戏就结束了。 /如果再添加几行实现计数器的代码,我们就可以确定在发生多少次此类事件后游戏将终止。/
- 该字段的标题,即 JFrame 的标题,用作仪表板显示,显示当前吃掉的苹果数量。并且如果蠕虫跑进了场地的框架或自己,它还会显示有关相应事件的信息。
1.
public enum Direction {
LEFT,
RIGHT,
DOWN,
UP;
}
2.
public class Piece {
private int x;
private int y;
private int width;
private int length;
private int numberOfApples;
public Piece(int x, int y, int width, int length) {
this.x = x;
this.y = y;
this.length = length;
this.width = width;
}
public void setNumberOfApples() {
this.numberOfApples += 1;
}
public int getNumberOfApples() {
return this.numberOfApples;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getWidth() {
return this.width;
}
public int getLength() {
return this.length;
}
/*we use the following two override methods in order to check when the worm runs into itself.
This checking is done through the method "public boolean runsIntoItself(Piece piece)" in class "Worm"*/
@Override
public boolean equals(Object object) {
if (this.getClass() == null) {
return false;
}
if (this.getClass() != object.getClass()) {
return false;
}
Piece compared = (Piece) object;
if (this.x != compared.x || this.y != compared.y
|| this.width != compared.width || this.length != compared.length) {
return false;
}
return true;
}
@Override
public int hashCode() {
if (this.getClass() == null) {
return 1;
}
return this.x + this.y + this.width + this.length;
}
@Override
public String toString() {
return "(" + this.x + ", " + this.y + ", " + this.width + ", " + this.length + ")";
}
}
3.
public class Apple extends Piece {
public Apple(int x, int y, int width, int length) {
super(x, y, width, length);
}
}
4.
import java.util.List;
import java.util.ArrayList;
import javax.swing.JFrame;
public class Worm extends Piece {
private int growth;
Direction direction;
private List<Piece> piecesWorm;
private JFrame frame;
public Worm(int x, int y, int width, int length, Direction originalDirection) {
super(x, y, width, length);
this.growth = 0;
this.direction = originalDirection;
this.piecesWorm = new ArrayList<Piece>();
this.piecesWorm.add(new Piece(x, y, width, length));
frame = new JFrame("Number of Apples: " + super.getNumberOfApples());
}
public JFrame getFrame() {
return this.frame;
}
public Direction getDirection() {
return this.direction;
}
public void setDirection(Direction dir) {
this.direction = dir;
}
public int getLength() {
return this.piecesWorm.size();
}
public List<Piece> getPieces() {
return this.piecesWorm;
}
/*this method takes the calculated new piece from method "calculateNewPiece"
and does the following:
# moves the worm(adds an element to the head and substructs an element from the tail)
# makes the worm grow(only adds an element to the head)
# terminates the game if the worm runs into itself*/
public void move() {
if (this.runsIntoItself(this.calculateNewPiece()) == false) {
if (this.piecesWorm.size() < 3) {
this.piecesWorm.add(this.calculateNewPiece());
} else if (this.piecesWorm.size() >= 3) {
if (this.growth == 0) {
this.piecesWorm.add(this.calculateNewPiece());
this.piecesWorm.remove(0);
} else if (this.growth == 1) {
this.piecesWorm.add(this.calculateNewPiece());
this.growth = 0;
}
}
} else {
this.frame.setTitle("The worm has run into itself!!! "
+ "Total Number of Apples: " + (this.getNumberOfApples()));
try {
Thread.sleep(10000);
} catch (Exception ex) {
}
System.exit(0);
}
}
/*this method calculates the next piece of the worm according to its direction*/
public Piece calculateNewPiece() {
// lpbm stands for "lastPieceBeforeMoving"
Piece lpbm = null;
Piece newPiece = null;
if (this.piecesWorm.size() == 1) {
lpbm = this.piecesWorm.get(0);
} else {
lpbm = this.piecesWorm.get(this.piecesWorm.size() - 1);
}
int x = lpbm.getX();
int y = lpbm.getY();
if (this.direction.equals(Direction.LEFT)) {
newPiece = new Piece(x - 1 - super.getWidth(), y, super.getWidth(), super.getLength());
} else if (this.direction.equals(Direction.RIGHT)) {
newPiece = new Piece(x + 1 + super.getWidth(), y, super.getWidth(), super.getLength());
} else if (this.direction.equals(Direction.DOWN)) {
newPiece = new Piece(x, y + 1 + super.getLength(), super.getWidth(), super.getLength());
} else if (this.direction.equals(Direction.UP)) {
newPiece = new Piece(x, y - 1 - super.getLength(), super.getWidth(), super.getLength());
}
return newPiece;
}
/*this method sets the conditions that determines whether the worm will grow or not when
the method "move()" is called*/
public void grow() {
this.growth = 1;
}
/*this method checks whether the worm runs into an apple*/
public boolean runsInto(Piece piece) {
Piece headPieceOfWorm = this.piecesWorm.get(this.piecesWorm.size() - 1);
if ((headPieceOfWorm.getX() + (headPieceOfWorm.getWidth())) < piece.getX()) {
return false;
} else if (headPieceOfWorm.getX() > (piece.getX() + piece.getWidth())) {
return false;
} else if ((headPieceOfWorm.getX() + (headPieceOfWorm.getWidth())) >= piece.getX()
&& (headPieceOfWorm.getX() <= (piece.getX() + piece.getWidth()))) {
if (headPieceOfWorm.getY() + (headPieceOfWorm.getLength()) < piece.getY()
|| (headPieceOfWorm.getY() > piece.getY() + (piece.getLength()))) {
return false;
}
}
return true;
}
/*this method checks whether the worm runs into itself*/
public boolean runsIntoItself(Piece piece) {
if (this.piecesWorm.contains(piece)) {
return true;
}
return false;
}
@Override
public String toString() {
return "(" + super.getX() + ", " + super.getY() + ", " + super.getWidth()
+ ", " + super.getLength() + ", " + this.getDirection() + ")";
}
}
5.
import java.util.Random;
import java.util.List;
public class AvoidOverlappingPieces {
/*this method calculates the initial position of the apple
so that the apple is within the field and it does not overlap with the worm*/
public Apple initialApple(Worm worm, int fieldWidth, int fieldLength, int pieceWidth, int pieceLength) {
Random random = new Random();
Apple apple = null;
while (true) {
int xApple = random.nextInt(fieldWidth - (pieceWidth));
int yApple = random.nextInt(fieldLength - (pieceLength));
apple = new Apple(xApple, yApple, pieceWidth, pieceLength);
if (xApple + (pieceWidth) < worm.getX()) {
break;
} else if (xApple > worm.getX() + pieceWidth) {
break;
} else if ((xApple + (pieceWidth) >= worm.getX())
&& (xApple <= worm.getX() + pieceWidth)) {
if (yApple + (pieceLength) < worm.getY()
|| (yApple > worm.getY() + (pieceLength))) {
break;
}
}
}
return apple;
}
/*this method calculates the position of the new apple, after the previous one is eaten
by the worm, so that the apple is within the field and it does not overlap with the worm*/
public Apple replaceEatenApple(Worm worm, List<Piece> piecesWorm, int fieldWidth, int fieldLength, int pieceWidth, int pieceLength) {
Random random = new Random();
Apple apple = null;
while (true) {
int xApple = random.nextInt(fieldWidth - (pieceWidth));
int yApple = random.nextInt(fieldLength - (pieceLength));
Apple newApple = new Apple(xApple, yApple, pieceWidth, pieceLength);
if (this.fieldExeptWorm(newApple, piecesWorm) == false) {
apple = newApple;
break;
}
}
return apple;
}
/*this method calculates the available space, taking account of the current
position of worm, and it is used in the method "replaceEatenApple(...)"*/
public boolean fieldExeptWorm(Piece piece, List<Piece> piecesWorm) {
int counter = 0;
for (int i = 0; i < piecesWorm.size(); i++) {
Piece wormPiece = piecesWorm.get(i);
if ((wormPiece.getX() + (wormPiece.getWidth())) < piece.getX()) {
counter++;
} else if (wormPiece.getX() > (piece.getX() + piece.getWidth())) {
counter++;
} else if ((wormPiece.getX() + (wormPiece.getWidth())) >= piece.getX()
&& (wormPiece.getX() <= (piece.getX() + piece.getWidth()))) {
if (wormPiece.getY() + (wormPiece.getLength()) < piece.getY()
|| (wormPiece.getY() > piece.getY() + (piece.getLength()))) {
counter++;
}
}
}
if (counter == piecesWorm.size()) {
return false;
}
return true;
}
}
6.
import java.util.Random;
public class WormGame {
private int fieldWidth;
private int fieldLength;
private int pieceWidth;
private int pieceLength;
private Worm worm;
private Apple apple;
private Random random;
/* class "AvoidOverlappingPieces" contains methods that calculate
the initial spot of the apple and its subsequent positions across the field*/
private AvoidOverlappingPieces properPieces;
protected int applesNumber;
public WormGame(int fieldWidth, int fieldLength) {
this.fieldWidth = fieldWidth;
this.fieldLength = fieldLength;
this.pieceWidth = (this.fieldWidth - 10) / 10;
this.pieceLength = (this.fieldLength - 10) / 10;
this.worm = new Worm(0, 0, pieceWidth, pieceLength, Direction.DOWN);
this.random = new Random();
this.properPieces = new AvoidOverlappingPieces();
this.apple = this.properPieces.initialApple(this.worm, this.fieldWidth, this.fieldLength, this.pieceWidth, this.pieceLength);
}
public int getfieldWidth() {
return this.fieldWidth;
}
public int getfieldLength() {
return this.fieldLength;
}
public Worm getWorm() {
return this.worm;
}
public Apple getApple() {
return this.apple;
}
/*this method accelerates the worm as it grows longer,
through calculating smaller and smaller values for the number
of milliseconds that are fed into "Thread.sleep(int number)",
implemented in the method "action()";*/
public int setDelay(Worm worm) {
int delay = 1500 / worm.getLength();
return delay;
}
/*this method takes the calculated new piece from method "calculateNewPiece"
from class "Worm" and does the following:
# checks whether the new piece of the worm is within the confines of the field.
# if the new piece is within these confines the method "move()" from class "Worm" is called.
# if the worm bupms into an apple, the method "grow()" from class "Worm" and
the method "replaceEatenApple(...)" from class "AvoidOverlappingPieces" are called.
# if the new piece is outside the confines of the field the game is terminated.*/
public void action() {
int x_nextStep = this.worm.calculateNewPiece().getX();
int y_nextStep = this.worm.calculateNewPiece().getY();
if (x_nextStep >= 0 && (x_nextStep + this.pieceWidth) <= this.fieldWidth
&& y_nextStep >= 0 && (y_nextStep + this.pieceLength) <= this.fieldLength) {
try {
Thread.sleep(this.setDelay(this.worm));
} catch (Exception ex) {
}
this.worm.move();
if (this.worm.runsInto(this.apple)) {
this.worm.setNumberOfApples();
this.getWorm().getFrame().setTitle("Number of Apples: " + (this.worm.getNumberOfApples()));
this.worm.grow();
this.apple = this.properPieces.replaceEatenApple(this.worm, this.worm.getPieces(), this.fieldWidth, this.fieldLength, this.pieceWidth, this.pieceLength);
}
} else {
this.getWorm().getFrame().setTitle("The worm has run into a wall!!! "
+ "Total Number of Apples: " + (this.worm.getNumberOfApples()));
try {
Thread.sleep(10000);
} catch (Exception ex) {
}
System.exit(0);
}
}
public int getNumberOfApples() {
return this.applesNumber;
}
}
7.
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
public class KeyboardListener implements KeyListener {
private WormGame wormGame;
public KeyboardListener(WormGame wormGame) {
this.wormGame = wormGame;
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
this.wormGame.getWorm().setDirection(Direction.LEFT);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
this.wormGame.getWorm().setDirection(Direction.RIGHT);
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
this.wormGame.getWorm().setDirection(Direction.DOWN);
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
this.wormGame.getWorm().setDirection(Direction.UP);
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
8.
import java.awt.Color;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.Timer;
public class DrawingBoard extends JPanel implements ActionListener {
private WormGame wormGame;
/*"Timer" sets the speed with which the worm moves.
Since in our case the worm accelerates with every eaten apple through
"Thread.sleep()" applied in method "action()", class "WormGame", the "Timer" here
sets the maximum speed.
In other words:
the initial speed (i.e. delay in the movement of the worm) is the sum of
the value of the integer in "Timer", which is 400, plus the value of the integer in
"Thread.sleep", which is 1500, totalling to 1900.
So, theoretically the value in "Thread.sleep" could be reduced to nearly zero, thus
the maximum speed of 400 milliseconds delay remains the upper speed limit.
Of course these values could be changed as per the desire of the player.*/
Timer tm = new Timer(400, this);
public DrawingBoard(WormGame wormGame) {
this.wormGame = wormGame;
super.setBackground(Color.WHITE);
}
/*this method paints the initial positions of the apple and the worm*/
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (Piece w : this.wormGame.getWorm().getPieces()) {
g.fill3DRect(w.getX(), w.getY(), w.getWidth(), w.getLength(), true);
}
g.setColor(Color.RED);
g.fillOval(this.wormGame.getApple().getX(), this.wormGame.getApple().getY(),
this.wormGame.getApple().getWidth(), this.wormGame.getApple().getLength());
tm.start();
}
/*this method paints the positions of the apple and the worm as they move across the board*/
public void actionPerformed(ActionEvent event) {
this.wormGame.action();
repaint();
}
}
9.
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.WindowConstants;
import javax.swing.JFrame;
public class UserInterface implements Runnable {
private JFrame frame;
private WormGame wormGame;
public UserInterface(WormGame wormGame) {
this.wormGame = wormGame;
}
public void run() {
frame = this.wormGame.getWorm().getFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.createCompoent(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
private void createCompoent(Container container) {
DrawingBoard board = new DrawingBoard(this.wormGame);
container.add(board);
KeyboardListener kL = new KeyboardListener(this.wormGame);
frame.addKeyListener(kL);
container.setPreferredSize(new Dimension(this.wormGame.getfieldWidth(), this.wormGame.getfieldLength()));
}
}
10.
import javax.swing.SwingUtilities;
public class Run {
public static void main(String[] args) {
WormGame game = new WormGame(510, 510);
UserInterface ui = new UserInterface(game);
SwingUtilities.invokeLater(ui);
}
}