java学习的第二个代码(飞行棋比赛-----龟兔赛跑),继上一个博客,对数组和Arrays的熟悉
前言
虽然这是一个渣渣写的关于龟兔赛跑的渣渣博客,但是,转载请注明出处。这里,我会简单介绍思路,然后附上源代码。
题目描述
写一个龟兔赛跑的程序,尽量考虑代码的延展性。乌龟和兔子在长度为100的跑道上赛跑,跑道为"- -",兔子的最大速度为6,乌龟的最大速度为3。跑道上每20个跑格内会随机出现1组特殊跑格,分别为地雷"@@"、传送门"=="、下坡"^^"、幸运星"**"、树"||"(就是你每20个格子内会出现五个不同的特殊跑格)。兔子和乌龟对特殊跑格的处理不同:
1. 兔子遇到地雷,返回到上两个地雷的位置,乌龟只返回上一个地雷位置;
2. 兔子遇到传送门,跳到下一个传送门位置,乌龟跳到下两个传送门位置;
3. 兔子遇到下坡无反应,乌龟向前走10步;
4. 兔子和乌龟遇到幸运星均再走一次;
5. 兔子遇到树休息3次,乌龟无反应;
其中地雷和传送门无联动现象(就是你炸回去了,你不会因为你现在是炸弹而继续往回炸),其他有联动现象(就是你捡到幸运星,然后你再走了一次之后又遇到幸运星,那就再走,直到你没遇到特殊跑格为止)。
此外,要求比赛过程由用户输入回车来控制
思路
1.创建跑道类(这里只介绍如何生成跑道,怎么把它写成一个类太简单了,我不做介绍):
1.1 先初始化一个无特殊跑格的跑道:
定义一个数组,元素为String类型,全部存储"- -"
使用Arrays工具类的void fill(type[] a , type val)静态方法将数组填充完成。
Arrays.fill(road, "--");
1.2再初始化特殊跑格
每20个跑格需要生成五个不同的随机数,这里使用乱序法。
- 我们先生成一个长度为20的数组,这个数组的元素值和其索引相同,即a[i] = i。然后我们设置一个循环,将这个数组内的数打乱,那么这个数组就是一个数据限制在0-19的随机数的长度为20数组。
int[] temp = new int[20];
for(int i = 0;i < 20;i++)//生成有序的0-19
{
temp[i]=i;
}
- 怎么打乱:我们可以生成两个随机数,让以这两个随机数作为索引的元素交换数据。但是,在这个程序中,我们需要的数据限制在0-19的随机数的长度为5数组。那么,我们只需要将前五个数固定为参与交换的其中一个数即可,即我们只生成一个随机数,而另一个随机数,我们直接指定为前五个数。
for(int j = 0;j<5;j++)//打乱前五个数,每次需要五个特殊格
{
int p = random.nextInt(20);
exchange = temp[j];
temp[j] = temp[p];
temp[p] = exchange;
}
-
我们需要每20个跑格生成一组特殊跑格,而100个跑格需要生成五组随机数,那么我们在循环外侧再写一层循环,用于控制生成五组随机跑格。而每组跑格的数据限制是不相同的,第一组是0-19,第二组是20-39,依次类推,那么我们可以使用外层循环的控制条件来转化数据范围。即
生成第一组特殊跑格时,外层循环控制条件i = 0,那 i * 20 + 生成0-19的随机数=限制在0-19的随机数;
生成第二组特殊跑格时,外层循环控制条件i = 1,那 i * 20 + 生成0-19的随机数=限制在20-39的随机数依次类推。
for(int i = 0;i < 5;i++)//每20个跑格生成五个特殊跑格,所以生成五次
{
for(int j = 0;j<5;j++)//打乱前五个数,每次需要五个特殊格
{
int p = random.nextInt(20);
exchange = temp[j];
temp[j] = temp[p];
temp[p] = exchange;
}
// 修改跑道
road[temp[0] + i * 20] = "**";
road[temp[1] + i * 20] = "==";
road[temp[2] + i * 20] = "@@";
road[temp[3] + i * 20] = "^^";
road[temp[4] + i * 20] = "||";
}
- 此外,为了便于传送门和地雷的跳转,我们另外使用两个数组来记录我们的地雷和传送门位置
protal[i] = temp[1] + i * 20;
landMine[i] =temp[2] + i * 20;
2.创建规则(定义一个接口)
我们的游戏规则使我们的小动物比赛,不同的游戏会有不同的规则,而我们的飞行棋游戏使小动物遇到特殊跑格时必须做出相应的动作或反应。所以,我在这里写了一个接口LudoPlayer,用于规范参赛者的行为
/**
* LudoPlayer接口<br>
* 作为飞行棋玩家,需要遵守的规则,不同游戏不同规则<br>
* 玩家可以在跑道上跑<br>
* 所有玩家会遇到幸运星 ** 、传送门==、地雷 @@、下坡 ^^、树 ||。玩家需要根据自己的能力作相应处理 <br>
* @author wantingting666@gmail.com
*
*/
public interface LudoPlayer {
void luck();//遇到幸运星
void portal(int[] nextPortal);//传送门
void landMine(int[] pastLandMine);//地雷
void downhill();//下坡
void tree();//树
}
3.创建参赛小动物
3.1为了程序的扩展,创建一个父类Animal
由于Animal类是一种更高级的抽象,我们不能生成他的对象(你说让我生成一只兔子,我还能知道长啥样,你现在要我生成一只动物,鬼知道是啥),所以,我们创建的是一个抽象类。我们将兔子和乌龟的特征和行为进行抽象,这里小动物会跑、会睡觉等就可以抽象出来。此外,在本游戏中,小动物都需要遵守规则,那么,我们让Animal类来实现我们的接口。
这里,我们会用到一种设计模式,就是缺省适配器模式,因为当兔子遇到下坡时不做反应,同样乌龟遇到树不做反应,更多的是,可能某种小动物对所有特殊跑格都不做反应,但是接口的方法必须全部重写,那怎么办,我们可以用其父类来实现,不过这里的实现是空实现(就是没有方法体,废话特别多),注意的是,我们的小动物遇到幸运星时的反应是相同的,所以,我们可以直接实现,而不是空实现
package runGame;
import java.util.Random;
/**
* Animal类<br>
* 所有动物的共有特征,该类不能有对象<br>
* @author wantingting666@gmail.com
*
*/
public abstract class Animal implements LudoPlayer{
protected String type;//物种
protected int maxSpeed;//最快速度
protected int position;//现在位置
protected boolean isWake;
/**
* 构造方法
*/
public Animal() {
// TODO 自动生成的构造函数存根
}
/**
* run方法<br>
* 动物均可以运动<br>
* 且动物运动起来,都是限制在最大速度内的<br>
*/
public void run()
{
if(!this.isWake)
{
return;
}
Random random = new Random();
int step=random.nextInt(maxSpeed) + 1;
position += step;
}
/**
* getPosition方法<br>.
* @return 当前位置
*/
public int getPosition() {
return position;
}
/**
* getType方法<br>
* @return 物种
*/
public String getType() {
return type;
}
/**
* getWake方法
* @return 动物是否睡觉
*/
public boolean getWake() {
return isWake;
}
/**
* setWake方法
* 动物醒来
*/
public void setWake() {
this.isWake=true;
}
@Override
public void luck() {
this.run();
}
@Override
public void downhill() {
}
@Override
public void tree() {
}
}
3.2写兔子类和乌龟类(就是继承我们的Animal类,怎么写类简单,不介绍)
这里只介绍,在遇到地雷和传送门时怎么跳转。我们在创建跑道的时候已经将特殊跑格进行了保存,所以,这里,我们只需要查询,小动物踩到的是第几个地雷或传送门即可。这里,我们使用Arrays的二分法int binarySearch(type[] a , tyep key)来查询,因为我们本身就是升序存储的,二分法会更加快。
int i = Arrays.binarySearch(nextPortal,this.position);
源代码
package runGame;
import java.util.Random;
/**
* Animal类<br>
* 所有动物的共有特征,该类不能有对象<br>
* @author wantingting666@gmail.com
*
*/
public abstract class Animal implements LudoPlayer{
protected String type;//物种
protected int maxSpeed;//最快速度
protected int position;//现在位置
protected boolean isWake;
/**
* 构造方法
*/
public Animal() {
// TODO 自动生成的构造函数存根
}
/**
* run方法<br>
* 动物均可以运动<br>
* 且动物运动起来,都是限制在最大速度内的<br>
*/
public void run()
{
if(!this.isWake)
{
return;
}
Random random = new Random();
int step=random.nextInt(maxSpeed) + 1;
position += step;
}
/**
* getPosition方法<br>.
* @return 当前位置
*/
public int getPosition() {
return position;
}
/**
* getType方法<br>
* @return 物种
*/
public String getType() {
return type;
}
/**
* getWake方法
* @return 动物是否睡觉
*/
public boolean getWake() {
return isWake;
}
/**
* setWake方法
* 动物醒来
*/
public void setWake() {
this.isWake=true;
}
@Override
public void luck() {
this.run();
}
@Override
public void downhill() {
}
@Override
public void tree() {
}
}
package runGame;
import java.util.Arrays;
/**
* Rabbit类<br>
* 物种之兔子<br>
* @author wantingting666@gmail.com
*
*/
public class Rabbit extends Animal {
public Rabbit() {
maxSpeed = 6;
type = "兔子";
position = 0;
this.isWake = true;
}
@Override
public void luck() {
this.run();
}
@Override
public void portal(int[] nextPortal) {
int i = Arrays.binarySearch(nextPortal,this.position);
this.position = (i <= nextPortal.length-2 ? nextPortal[i+1] : 100);
}
@Override
public void landMine(int[] pastLandMine) {
int i = Arrays.binarySearch(pastLandMine,this.position);
this.position=(i >= 2?pastLandMine[i-2] : 0);
}
@Override
public void tree() {
this.isWake = false;
}
}
package runGame;
import java.util.Arrays;
/**
* Tortoise类<br>
* 物种之乌龟<br>
* @author wantingting666@gmail.com
*
*/
public class Tortoise extends Animal {
public Tortoise() {
maxSpeed = 3;
type = "乌龟";
position = 0;
isWake = true;
}
@Override
public void portal(int[] nextPortal) {
int i = Arrays.binarySearch(nextPortal,this.position);
this.position = ( i <= nextPortal.length-3 ? nextPortal[i+2] : 100);
}
@Override
public void landMine(int[] pastLandMine) {
int i = Arrays.binarySearch(pastLandMine,this.position);
this.position = ( i >= 1 ? pastLandMine[i-1] : 0);
}
@Override
public void downhill() {
this.position += 10;
}
}
package runGame;
/**
* LudoPlayer接口<br>
* 作为飞行棋玩家,需要遵守的规则,不同游戏不同规则<br>
* 玩家可以在跑道上跑<br>
* 所有玩家会遇到幸运星 ** 、传送门==、地雷 @@、下坡 ^^、树 ||。玩家需要根据自己的能力作相应处理 <br>
* @author wantingting666@gmail.com
*
*/
public interface LudoPlayer {
void luck();//遇到幸运星
void portal(int[] nextPortal);//传送门
void landMine(int[] pastLandMine);//地雷
void downhill();//下坡
void tree();//树
}
package runGame;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
/**
* Ludo类<br>
* 整个游戏的过程,玩家、规则、游戏设备的综合<br>
* @author wantingting666@gmail.com
*
*/
public class Ludo {
/**
* 游戏需要跑道,玩家
*/
private Track track = new Track();
private Tortoise tortoise = new Tortoise();
private Rabbit rabbit = new Rabbit();
private static int flag = 0;//记录兔子是否应该睡觉
/**
* Track类<br>
* 跑道上拥有多种障碍和奖励<br>
* 但是跑道不能左右玩家的动向<br>
* 玩家的动向由游戏决定<br>
* 玩家的具体动作由玩家决定<br>
* 举例说明,跑道仅供玩家使用,当遇到地雷时,游戏决定玩家需要被处罚,但是处罚轻重由玩家的身体素质(这里是物种)决定<br>
*/
private class Track {
public String[] road = new String[100];
//记录特殊格子,传送和地雷用
public int[] protal = new int[5];
public int[] landMine = new int[5];
/**
* 构造方法<br>
* 初始化跑道<br>
*/
public Track() {
Arrays.fill(road, "--");
Random random = new Random();
int exchange;
int[] temp = new int[20];
for(int i = 0;i < 20;i++)//生成有序的0-19
{
temp[i]=i;
}
for(int i = 0;i < 5;i++)//每20个跑格生成五个特殊跑格,所以生成五次
{
for(int j = 0;j<5;j++)//打乱前五个数,每次需要五个特殊格
{
int p = random.nextInt(20);
exchange = temp[j];
temp[j] = temp[p];
temp[p] = exchange;
}
// 记录特殊跑格和修改跑道
road[temp[0] + i * 20] = "**";
road[temp[1] + i * 20] = "==";
road[temp[2] + i * 20] = "@@";
road[temp[3] + i * 20] = "^^";
road[temp[4] + i * 20] = "||";
protal[i] = temp[1] + i * 20;
landMine[i] =temp[2] + i * 20;
}
//打印跑道
System.out.println("初始态:");
for(String i : road)
{
System.out.print(i);
}
}
}
/**
* judge方法<br>
* 判断玩家动向<br>
* 仅供内部使用<br>
* @param 玩家
*/
private void judge(Animal animal) {
if(animal.getPosition() >= 100)
{
System.out.println(animal.getType() + "到达终点");
return;
}
switch (track.road[animal.getPosition()]) {
case "**":
animal.luck();
System.out.println(animal.type + "捡到幸运星");
judge(animal);
break;
case "==":
System.out.println(animal.type + "遇到传送门");
animal.portal(this.track.protal);
break;
case "@@":
System.out.println(animal.type + "踩到地雷");
animal.landMine(this.track.landMine);
break;
case "^^":
System.out.println(animal.type + "遇到下坡");
if(animal.type.equals("乌龟"))
{
animal.downhill();
judge(animal);
}
break;
case "||":
if(animal.getType().equals("乌龟"))
{
System.out.println(animal.type + "遇到树");
}
else if(flag == 0)
{
animal.tree();
flag = 3;
}
if(animal.getType().equals("兔子") && flag != 0)
{
System.out.println("兔子休息");
flag--;
if(flag == 0)
{
animal.setWake();
}
}
break;
default:
System.out.println(animal.type + "正常跑");
break;
}
}
/**
* run方法<br>
* 游戏的过程<br>
*/
public void run()
{
Scanner scanner = new Scanner(System.in);
for(int i = 1;;i++)
{
scanner.nextLine();
if(this.tortoise.getPosition() >= 100 || this.rabbit.getPosition() >= 100)
{
System.out.println((this.tortoise.getPosition() > this.rabbit.getPosition() ? "乌龟" : "兔子") + "到达终点");
System.out.println("游戏结束");
break;
}
else
{
System.out.println("回合" + i + ": ");
this.tortoise.run();
this.rabbit.run();
judge(this.tortoise);
judge(this.rabbit);
print();
}
}
scanner.close();
}
/**
* print方法<br>
* 打印当前赛程<br>
*/
public void print()
{
for(int i = 0;i < this.track.road.length;i++)
{
if(i == rabbit.getPosition())
{
System.out.print("tu");
}
else if(i == tortoise.getPosition())
{
System.out.print("wu");
}
else
{
System.out.print(this.track.road[i]);
}
}
}
public static void main(String[] args) {
Ludo ludo = new Ludo();
ludo.run();
}
}