前段时间发现同学们用纸玩起了一个打发时间的小游戏,感觉规则挺有意思的。
每个人有一片10*10的海域和五艘船,大小分别是1*1,1*2,1*2,2*2,2*2,玩家先安排自己的船的位置,然后轮流攻击对方的海域。玩家对对方的海域一无所知,只能通过猜测进行攻击。如果击中了对方的船,那么可以再攻击一次,击沉对方所有的船的玩家胜利。
规则非常简单,但是由于我是第一次写这种游戏项目,所以感觉还是挺麻烦的。
前段时间刚刚学会Java和面向对象,于是尝试用它们来写一下。有点大材小用了?
闲话少说,整个项目由4部分组成:
- 总控台
- 海域管理
- 船只
- 其他控制
我们一个一个来说。
船只部分
/* Ship.java */
package ship;
public abstract class Ship {
protected int x, y; // 左上角坐标
protected int live = -1;
Ship(){}
Ship(int x, int y) {
this.x = x;
this.y = y;
}
public void setPosition(int x, int y) {
this.x = x; this.y = y;
return;
}
public int[] getPosition() {
int[] ret = {x, y};
return ret;
}
public int live() {
return live;
}
public void shooted() {
live--;
}
public abstract int[][] getSpace();
}
这个类是所有类型的船只的父类(一个抽象类),包含了船的坐标,位置,基本操作,生命值(表示这艘船被打废了多少)等等(还进行了一些没必要的封装)
/* OneTimesOneShip.java */
package ship;
public class OneTimesOneShip extends Ship {
public OneTimesOneShip(int x, int y) {
super(x, y);
this.x = x;
this.y = y;
live = 1;
}
@Override
public int[][] getSpace() {
return new int[][] {{x, y}};
}
}
标准的1*1船只
/* OneTimesTwoShip.java */
package ship;
public class OneTimesTwoShip extends Ship {
private boolean direction = true; // true 横向 false 竖向
public OneTimesTwoShip(int x, int y, int direction) {
super(x, y);
this.x = x;
this.y = y;
live = 2;
this.direction = direction == 1 ? true : false;
}
public boolean direction() {
return direction;
}
public void setDirection(boolean direction) {
this.direction = direction;
}
@Override
public int[][] getSpace() {
if (direction) {
return new int[][]{{x, y}, {x, y+1}};
} else {
return new int[][]{{x, y}, {x+1, y}};
}
}
}
标准的1*2船只,因为1*2的船可以横着或竖着放,所以多写了一点东西
/* TwoTimesTwoShip.java */
package ship;
public class TwoTimesTwoShip extends Ship {
public TwoTimesTwoShip(int x, int y) {
super(x, y);
this.x = x;
this.y = y;
live = 4;
}
@Override
public int[][] getSpace() {
return new int[][]{{x, y}, {x+1, y}, {x, y+1}, {x+1, y+1}};
}
}
标准的2*2船只,整体实现和1*1差不多
海域
/* Map.java */
import ship.*;
public class Map {
public char[][] ForOwner = new char[11][11];
public char[][] ForEnemy = new char[11][11];
public OneTimesOneShip Ship11;
public OneTimesTwoShip Ship12A;
public OneTimesTwoShip Ship12B;
public TwoTimesTwoShip Ship22A;
public TwoTimesTwoShip Ship22B;
Map() {
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
ForOwner[i][j] = ' ';
ForEnemy[i][j] = ' ';
}
}
}
public void fillShip(Ship ship) {
int x = ship.getPosition()[0], y = ship.getPosition()[1];
if (ship instanceof OneTimesOneShip) {
ForOwner[x][y] = '@';
} else if (ship instanceof OneTimesTwoShip) {
OneTimesTwoShip ship2 = (OneTimesTwoShip)ship;
if (ship2.direction()) {
ForOwner[x][y] = ForOwner[x][y+1] = '@';
} else {
ForOwner[x][y] = ForOwner[x+1][y] = '@';
}
} else if (ship instanceof TwoTimesTwoShip) {
ForOwner[x][y] = ForOwner[x+1][y] = ForOwner[x][y+1] = ForOwner[x+1][y+1] = '@';
}
}
public boolean equal(int[]a, int[]b) {
return a[0] == b[0] && a[1] == b[1];
}
public int shoot(int x, int y) {
if (x < 1 || x > 10 || y < 1 || y > 10) { // 超出边界
return -1;
} else if (ForOwner[x][y] == 'x' || ForOwner[x][y] == '#') { // 已经射过
return -2;
} else { // 非出错情况
if (ForOwner[x][y] == '@') { // 打中了
int[] pos = {x, y};
ForOwner[x][y] = '#'; // 打中的标识
ForEnemy[x][y] = '#';
if (equal(Ship11.getPosition(), pos)) { // 打中1*1了
Ship11.shooted();
return 111; // 1*1沉了
} else if (equal(Ship12A.getPosition(), pos) || equal(Ship12A.getSpace()[1], pos)) { // 打中1*2第一艘了
Ship12A.shooted();
if (Ship12A.live() != 0) {
return 1210; // 1*2第一艘中了没沉
} else {
return 1211; // 1*2第一艘沉了
}
} else if (equal(Ship12B.getPosition(), pos) || equal(Ship12B.getSpace()[1], pos)) { // 打中1*2第二艘了
Ship12B.shooted();
if (Ship12B.live() != 0) {
return 1220; // 1*2第二艘中了没沉
} else {
return 1221; // 1*2第二艘沉了
}
} else { // 打中的是2*2
int[][] space1 = Ship22A.getSpace();
int[][] space2 = Ship22B.getSpace();
if (equal(space1[0], pos) || equal(space1[1], pos) || equal(space1[2], pos) || equal(space1[3], pos)) { // 打中2*2第一艘了
Ship22A.shooted();
if (Ship22A.live() != 0) {
return 2210; // 2*2第一艘中了没沉
} else {
return 2211; // 2*2第一艘沉了
}
} else if (equal(space2[0], pos) || equal(space2[1], pos) || equal(space2[2], pos) || equal(space2[3], pos)) { // 打中2*2第二艘了
Ship22B.shooted();
if (Ship22B.live() != 0) {
return 2220; // 2*2第二艘中了没沉
} else {
return 2221; // 2*2第二艘沉了
}
}
}
} else { // 没打中
ForOwner[x][y] = 'x';
ForEnemy[x][y] = 'x';
return 0; // 没打中
}
}
return -114514;
}
public void DisplayMap() {
this.DisplayPlayerSight(this);
}
public void DisplayPlayerSight(Map b) {
ColorControler color = new ColorControler();
System.out.print("·---·---·---·---·---·---·---·---·---·---· ");
System.out.println("·---·---·---·---·---·---·---·---·---·---·");
/* */
for (int i = 1; i < 11; i++) {
System.out.print("|");
for (int j = 1; j < 11; j++) {
System.out.print(" ");
if (ForOwner[i][j] == ' ') System.out.print(' ');
if (ForOwner[i][j] == '@') System.out.print(color.GREEN+"@"+color.RESET);
if (ForOwner[i][j] == 'x') System.out.print(color.CYAN+'x'+color.RESET);
if (ForOwner[i][j] == '#') System.out.print(color.YELLOW+"#"+color.RESET);
System.out.print(" |");
}
System.out.print(" |");
for (int j = 1; j < 11; j++) {
System.out.print(' ');
if (b.ForEnemy[i][j] == ' ') System.out.print(' ');
if (b.ForEnemy[i][j] == '@') System.out.print(color.GREEN+"@"+color.RESET);
if (b.ForEnemy[i][j] == 'x') System.out.print(color.CYAN+'x'+color.RESET);
if (b.ForEnemy[i][j] == '#') System.out.print(color.YELLOW+"#"+color.RESET);
System.out.print(" |");
}
System.out.print("\n·---·---·---·---·---·---·---·---·---·---·");
System.out.println(" ·---·---·---·---·---·---·---·---·---·---·");
}
}
public boolean dead() {
return Ship11.live()==0 && Ship12A.live()==0 && Ship12B.live()==0 && Ship22A.live()==0 && Ship22B.live()==0;
}
}
这是一个刻画某玩家的海域的类,可用的方法有放置一艘船(指在字符表示的海域中放置),进行一次攻击,展示海域(具体为什么这么写可以看后面)
ColorControler类是用来控制输出的颜色用的,为了让玩家更清晰的看到海域的情况。
/* ColorControler.java */
public class ColorControler {
public final String RESET = "\u001B[0m";
public final String BLACK = "\u001B[30m";
public final String RED = "\u001B[31m";
public final String GREEN = "\u001B[32m";
public final String YELLOW = "\u001B[33m";
public final String BLUE = "\u001B[34m";
public final String PURPLE = "\u001B[35m";
public final String CYAN = "\u001B[36m";
public final String WHITE = "\u001B[37m";
}
总控台
/* MainControler.java */
import ship.*;
import java.io.*;
import java.util.Random;
public class MainControler {
public static void main(String[] args) {
while (true) {
cls();
Map mapa = new Map();
Map mapb = new Map();
System.out.println("海战 by AlexNotFoundBNDS");
System.out.println("请选择你的游戏模式:");
System.out.println("1. 单人模式");
System.out.println("2. 对战模式");
System.out.println("3. 帮助");
System.out.println("4. 退出游戏");
int result = ReadInt();
cls();
if (result == 1) { // 单人模式
boolean flag = false;
System.out.println("设置你的船的位置");
mapa = InitializeMap();
cls();
mapb = RandomMap();
Random r = new Random();
cls();
System.out.println("你的船只位置设置完成");
while (true) {
int x = 0, y = 0;
do {
System.out.println(" 你的地图 电脑的地图 ");
mapa.DisplayPlayerSight(mapb);
System.out.println("请输入你想打的位置:");
x = ReadInt(); y = ReadInt();
cls();
result = mapb.shoot(x, y);
System.out.println(ExplainShoot(result));
if (result == 0) {
break;
}
if (mapa.dead()) {
System.out.println("电脑赢了!\n");
System.out.println("你的地图:");
mapa.DisplayMap();
System.out.println("\n电脑的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
} else if (mapb.dead()) {
System.out.println("你赢了!\n");
System.out.println("你的地图:");
mapa.DisplayMap();
System.out.println("\n电脑的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
}
} while (true);
if (flag == true) break;
do {
if (result % 10 == 0 && result != 0) {
switch (r.nextInt(4)) {
case 0:
x++;
break;
case 1:
x--;
break;
case 2:
y++;
break;
case 3:
y--;
break;
}
} else {
x = r.nextInt(10)+1; y = r.nextInt(10)+1;
}
result = mapa.shoot(x, y);
System.out.println("电脑:攻击("+x+", "+y+"),"+ExplainShoot(result));
if (result == 0) {
break;
}
if (mapa.dead()) {
System.out.println("电脑赢了!\n");
System.out.println("你的地图:");
mapa.DisplayMap();
System.out.println("\n电脑的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
} else if (mapb.dead()) {
System.out.println("你赢了!\n");
System.out.println("你的地图:");
mapa.DisplayMap();
System.out.println("\n电脑的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
}
} while (true);
if (flag == true) break;
}
} else if (result == 2) { // 对战模式
boolean flag = false;
System.out.println("请玩家1到电脑前");
System.out.println("设置玩家1的船的位置");
mapa = InitializeMap();
cls();
System.out.println("玩家1船只位置设置完成");
System.out.println("请玩家2到电脑前");
pause();
cls();
System.out.println("设置玩家2的船的位置");
mapb = InitializeMap();
cls();
System.out.println("玩家2船只位置设置完成");
while (true) {
int x = 0, y = 0;
System.out.println("请玩家1到电脑前");
pause();
cls();
do {
System.out.println("玩家1");
System.out.println(" 你的地图 对手的地图 ");
mapa.DisplayPlayerSight(mapb);
System.out.println("请输入你想打的位置:");
x = ReadInt(); y = ReadInt();
cls();
result = mapb.shoot(x, y);
System.out.println(ExplainShoot(result));
if (result == 0) {
break;
}
if (mapa.dead()) {
System.out.println("玩家2赢了!\n");
System.out.println("玩家1的地图:");
mapa.DisplayMap();
System.out.println("\n玩家2的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
} else if (mapb.dead()) {
System.out.println("玩家1赢了!\n");
System.out.println("玩家1的地图:");
mapa.DisplayMap();
System.out.println("\n玩家2的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
}
} while (true);
if (flag == true) break;
System.out.println("请玩家2到电脑前:");
pause();
cls();
do {
System.out.println("玩家2");
System.out.println(" 你的地图 对手的地图 ");
mapb.DisplayPlayerSight(mapa);
System.out.println("请输入你想打的位置:");
x = ReadInt(); y = ReadInt();
cls();
result = mapa.shoot(x, y);
System.out.println(ExplainShoot(result));
if (result == 0) {
break;
}
if (mapa.dead()) {
System.out.println("玩家2赢了!\n");
System.out.println("玩家1的地图:");
mapa.DisplayMap();
System.out.println("\n玩家2的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
} else if (mapb.dead()) {
System.out.println("玩家1赢了!\n");
System.out.println("玩家1的地图:");
mapa.DisplayMap();
System.out.println("\n玩家2的地图:");
mapb.DisplayMap();
flag = true;
pause();
break;
}
} while (true);
if (flag == true) break;
}
} else if (result == 3) { // 帮助
System.out.println("这是一款规则简单且容易操作的字符画策略游戏");
System.out.println("每个人有一片10*10的海域和五艘船");
System.out.println("大小分别是1*1,1*2,1*2,2*2,2*2");
System.out.println("玩家先安排自己的船的位置,然后轮流攻击对方的海域");
System.out.println("玩家对对方的海域一无所知,只能通过猜测进行攻击");
System.out.println("如果击中了对方的船,那么可以再攻击一次");
System.out.println("击沉对方所有的船的玩家胜利");
pause();
} else if (result == 4) { // 游戏结束
System.out.println("游戏结束!");
System.out.println("感谢您的游玩!");
System.out.println("再见!");
pause();
break;
}
}
return;
}
static String ExplainShoot(int result) {
switch (result) {
case -2:
return "已经打过了!";
case -1:
return "超出地图范围!";
case 0:
return "没有击中!";
case 111:
return "击沉了1*1的船!";
case 1210:
return "击中了第一艘1*2的船!";
case 1211:
return "击沉了第一艘1*2的船!";
case 1220:
return "击中了第二艘1*2的船!";
case 1221:
return "击沉了第二艘1*2的船!";
case 2210:
return "击中了第一艘2*2的船!";
case 2211:
return "击沉了第一艘2*2的船!";
case 2220:
return "击中了第二艘2*2的船!";
case 2221:
return "击沉了第二艘2*2的船!";
default:
return "未知错误!";
}
}
static Map InitializeMap() {
int x = 0, y = 0;
Map map = new Map();
System.out.println("在设置中,请输入你的船只左上角的坐标(1-10)");
System.out.println("船只范围不可超出地图边界");
do {
System.out.println("请设置1*1的船的位置:");
x = ReadInt(); y = ReadInt();
if (x<1 || x>10 || y<1 || y>10)
System.out.println("超出地图范围!");
else break;
} while (true);
map.Ship11 = new OneTimesOneShip(x, y);
map.fillShip(map.Ship11);
int direction = -1;
do {
System.out.println("请输入第一艘1*2的船的位置:");
x = ReadInt(); y = ReadInt();
System.out.println("请输入第一艘1*2的船的方向(1表示横向,0表示竖向):");
direction = ReadInt();
if (x<1 || y<1 || (x>10 || y>9) && direction==1 || (x>9 || y>10) && direction==0 || direction!=0 && direction!=1)
System.out.println("超出地图范围!");
else if (map.ForOwner[x][y]=='@' || direction==1 && map.ForOwner[x][y+1]=='@' || direction==0 && map.ForOwner[x+1][y]=='@')
System.out.println("与先前船只重叠!");
else break;
} while (true);
map.Ship12A = new OneTimesTwoShip(x, y, direction);
map.fillShip(map.Ship12A);
do {
System.out.println("请输入第二艘1*2的船的位置:");
x = ReadInt(); y = ReadInt();
System.out.println("请输入第二艘1*2的船的方向(1表示横向,0表示竖向):");
direction = ReadInt();
if (x < 1 || y < 1 || (x > 10 || y > 9) && direction == 1 || (x > 9 || y > 10) && direction == 0 || direction != 0 && direction != 1)
System.out.println("超出地图范围!");
else if (map.ForOwner[x][y]=='@' || direction==1 && map.ForOwner[x][y+1]=='@' || direction==0 && map.ForOwner[x+1][y]=='@')
System.out.println("与先前船只重叠!");
else break;
} while (true);
map.Ship12B = new OneTimesTwoShip(x, y, direction);
map.fillShip(map.Ship12B);
do {
System.out.println("请输入第一艘2*2的船的位置:");
x = ReadInt(); y = ReadInt();
if (x < 1 || x > 9 || y < 1 || y > 9)
System.out.println("超出地图范围!");
else if (map.ForOwner[x][y]=='@' || map.ForOwner[x+1][y]=='@' || map.ForOwner[x][y+1]=='@' || map.ForOwner[x+1][y+1]=='@')
System.out.println("与先前船只重叠!");
else break;
} while (true);
map.Ship22A = new TwoTimesTwoShip(x, y);
map.fillShip(map.Ship22A);
do {
System.out.println("请输入第二艘2*2的船的位置:");
x = ReadInt(); y = ReadInt();
if (x < 1 || x > 9 || y < 1 || y > 9)
System.out.println("超出地图范围!");
else if (map.ForOwner[x][y]=='@' || map.ForOwner[x+1][y]=='@' || map.ForOwner[x][y+1]=='@' || map.ForOwner[x+1][y+1]=='@')
System.out.println("与先前船只重叠!");
else break;
} while (true);
map.Ship22B = new TwoTimesTwoShip(x, y);
map.fillShip(map.Ship22B);
return map;
}
static Map RandomMap() {
int x = 0, y = 0;
Map map = new Map();
Random r = new Random();
do {
x = r.nextInt(10)+1; y = r.nextInt(10)+1;
if (x<1 || x>10 || y<1 || y>10) {}
else break;
} while (true);
map.Ship11 = new OneTimesOneShip(x, y);
map.fillShip(map.Ship11);
int direction = -1;
do {
x = r.nextInt(10)+1; y = r.nextInt(10)+1; direction = r.nextInt(1);
if (x<1 || y<1 || (x>10 || y>9) && direction==1 || (x>9 || y>10) && direction==0 || direction!=0 && direction!=1)
continue;
else if (map.ForOwner[x][y]=='@' || direction==1 && map.ForOwner[x][y+1]=='@' || direction==0 && map.ForOwner[x+1][y]=='@')
continue;
else break;
} while (true);
map.Ship12A = new OneTimesTwoShip(x, y, direction);
map.fillShip(map.Ship12A);
do {
x = r.nextInt(10)+1; y = r.nextInt(10)+1; direction = r.nextInt(1);
if (x < 1 || y < 1 || (x > 10 || y > 9) && direction == 1 || (x > 9 || y > 10) && direction == 0 || direction != 0 && direction != 1)
continue;
else if (map.ForOwner[x][y]=='@' || direction==1 && map.ForOwner[x][y+1]=='@' || direction==0 && map.ForOwner[x+1][y]=='@')
continue;
else break;
} while (true);
map.Ship12B = new OneTimesTwoShip(x, y, direction);
map.fillShip(map.Ship12B);
do {
x = r.nextInt(9)+1; y = r.nextInt(9)+1;
if (map.ForOwner[x][y]=='@' || map.ForOwner[x+1][y]=='@' || map.ForOwner[x][y+1]=='@' || map.ForOwner[x+1][y+1]=='@')
continue;
else break;
} while (true);
map.Ship22A = new TwoTimesTwoShip(x, y);
map.fillShip(map.Ship22A);
do {
x = r.nextInt(9)+1; y = r.nextInt(9)+1;
if (map.ForOwner[x][y]=='@' || map.ForOwner[x+1][y]=='@' || map.ForOwner[x][y+1]=='@' || map.ForOwner[x+1][y+1]=='@')
continue;
else break;
} while (true);
map.Ship22B = new TwoTimesTwoShip(x, y);
map.fillShip(map.Ship22B);
return map;
}
static int ReadInt() {
BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
int x = 0;
boolean flag = false;
do {
try { // 读入到x
x = Integer.parseInt(read.readLine());
flag = true;
} catch (NumberFormatException e) {
if (e.getMessage().indexOf("For input string:") == -1)
System.err.println("抛出错误;"+e);
} catch (Exception e) {
System.err.println("抛出错误:"+e);
continue;
}
if (flag == true) break;
} while (true);
return x;
}
static void cls() {
try {
new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
} catch (Exception e) {
System.err.println("抛出错误:"+e);
}
}
static void pause() {
BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
System.out.println("按回车以继续");
try {
read.readLine();
} catch (Exception e) {
System.err.println("抛出错误:"+e);
}
}
}
这里是游戏的整体控制类,实现逻辑并不难,但是花了我很长时间。
为了让玩家体验更舒适,我手写了cls和pause,作用同cmd中的命令clear和pause。
如果是Linux或Mac用户想运行的话需要改一下cls函数,具体更改方法可以在C站查询ProcessBuilder的使用。
整体项目就说明完成了,如果要体验的话可以在下方链接下载:
链接:https://pan.baidu.com/s/1GFQWvckxzAM5aF7H5rk3sw
提取码:csdn
百度网盘
制作不易,给个赞呗!