弎问笔录28 之 狐狸和兔子(一)

按计划本来是昨天就应该写好发上来的了,可是突然遭遇感冒,头疼疼的就耽搁了,不好意思啊 ,今天大步赶上,^_^;

游戏规则是这样的:

  1. 狐狸和兔子都有年龄,到了一定年龄死掉;

  2. 狐狸可以随机决定吃周围的一只兔子,吃一次年龄上限就会提高一次;

  3. 狐狸和兔子会随机生个baby;

  4. 狐狸和兔子会向周围随机移动;

这个游戏可以看成是细胞自动机游戏的进阶版。游戏窗口是分割成一个个的网格,这些网格里的对象都统一交由一个数据管理类(Field)来管理实现互动,这些互动包括随机移动(move),捕食(feed),繁殖(breed)。然后这些数据更新好后将会由一个表现类(View)将其画(paint )出来。狐狸(fox)和(rabbit)对象都会放进网格当中去,因此创建一个接口(ICell),让狐狸和兔子实现这个接口。这样就可以把狐狸和兔子放进网格里去,并交由Field对象来管理。同时狐狸和兔子都存在共同的特性,例如年龄,都存在年龄上限,都会移动,繁殖等,因此可以创建一个Animal类,并让狐狸和兔子继承(extends)它。

随手画了个结构简图(好粗糙):

111232_bQei_2559261.jpg

出来一个效果是这样的(红色的是狐狸,黑色的是兔子(红狐黑兔)):105620_IgEV_2559261.jpg

 

package field;
/**
 * 为什么创建Location呢?
 * 因为根据游戏规则,狐狸和兔子会向周围空的位置移动,因此需要一个类来记录周围位置的坐标;
 * @author ItigerU
 *
 */
public class Location {
 private int row;
 private int col;
 public Location(int row, int col) {
  super();
  this.row = row;
  this.col = col;
 }
 public int getRow() {
  return row;
 }
 public int getCol() {
  return col;
 }
}

创建接口,狐狸和兔子通过这个接口放到Field里面去,Field对象通过管理ICell来管理狐狸和兔子:

package ICell;
/**
 * 这个接口只定义了一个方法,就是把让实现了这个接口的子类自己把自己给画出来;
 */
import java.awt.Graphics;
public interface ICell {
 void draw(Graphics g, int x, int y, int size);
}

创建好接口后创建Animal类:

package animal;
import java.util.List;
import field.Location;
/**1. 为什么Animal是抽象类;
 * Animal是狐狸和兔子的父类,实现了共同的属性和方法;例如年龄,年龄上限,移动,捕食,繁殖;
 * 繁殖需要每个子类重写(overridde),因为狐狸和兔子的繁殖概率不一样。所以将它定义成抽象方法;
 * 所以Animal是抽象类;
 * @author ItigerU
 *
 */
public abstract class Animal {
 private int age;
 private int ageLimit;
 private int breedableAge;
 private boolean isAlive = true;
 //给子类设定年龄上限和生育年龄
 public Animal(int ageLimit, int breedableAge) {
  this.ageLimit = ageLimit;
  this.breedableAge = breedableAge;
 }
 public int getAge() {
  return age;
 }
 public boolean isAlive() {
  return isAlive;
 }
 //该方法用来实现颜色的渐变;
 public double getAgePercent() {
  return (double)age / ageLimit;
 }
 
 public boolean isbreedable() {
  return age >= breedableAge;
 }
 //生长
 public void grow() {
  age ++;
  if(age > ageLimit) {
   this.die();
  }
 }
 //死亡
 public void die() {
  this.isAlive = false;
 }
 //随机繁殖;返回的一定是一个Animal啊...
 public abstract Animal breed();
 //随机移动,返回的是一个位置(确定移动到那个点的位置)。传入某点坐标和周围空的位置;
 public Location move(int row, int col, Location[] freeAdj) {
  Location ret = null;
  if(freeAdj.length > 0 && Math.random() < 0.12) {
   ret = freeAdj[(int)(Math.random() * freeAdj.length)];
  }
  return ret;
 }
 /*
  * 随机捕食;返回的是一个Animal对象,传入的是某点坐标和周围所有(意味着这是容器)的对象;
  * 需要狐狸重写,因为只有狐狸才捕食;
  */
 public Animal feed(int row, int col, List<Animal> fed) {
  return null;
 }
 //根据规则:狐狸每捕食一次年龄上限加一次;
 public void longerLife(int inc) {
  ageLimit += inc;
 }
 
 @Override
 public String toString() {
  return "" + age + " : " + (this.isAlive ? "isAlive" : "dead");
 }
 
}

创建好Animal类后再把狐狸和兔子两个子类给创建好:

package animal;
import java.awt.Color;
import java.awt.Graphics;
import java.util.List;
import ICell.ICell;
public class Fox extends Animal implements ICell {
 public Fox() {
  super(20, 4);
 }
 @Override
 public Animal breed() {
  Animal ret = null;
  if(Math.random() < 0.05) {
   ret = new Fox();
  }
  return ret;
 }
 @Override
 public void draw(Graphics g, int x, int y, int size) {
  int alpha = (int)((1 - this.getAgePercent()) * 255);
  g.setColor(new Color(0, 0, 0, alpha));
  g.fillRect(x, y, size, size);
 }
 //捕食,返回一个Animal.传入该点坐标,周围可捕食的对象;年龄上限加2;
 public Animal feed(int row, int col, List<Animal> feed){
  Animal ret = null;
  if(Math.random() < 0.05) {
   ret = feed.get((int)(Math.random() * feed.size()));
   this.longerLife(2);
  }
  return ret;
 }
 @Override
 public String toString() {
  return "Fox " + super.toString();
 }
}
package animal;
import java.awt.Color;
import java.awt.Graphics;
import ICell.ICell;
public class Rabbit extends Animal implements ICell {
 public Rabbit() {
  super(10, 2);
 }
 @Override
 public void draw(Graphics g, int x, int y, int size) {
  int alpha = (int)((1 - this.getAgePercent()) * 255);
  g.setColor(new Color(255, 0, 0, alpha));
  g.fillRect(x, y, size, size);
 }
 @Override
 public Animal breed() {
  Animal ret = null;
  if(Math.random() < 0.12) {
   ret = new Rabbit();
  }
  return ret;
 }
 @Override
 public String toString() {
  return "Rabbit: " + super.toString();
 }
}

第一个可以看到是Fox,第二个是Rabbit,它们之间差不多,但是Fox重写feed()方法,Rabbit没有。因为按游戏规则,Rabbit没有吃的说法,因此当调用Rabbit去feed的时候,是调用了父类的feed()方法,此时返回一个null;

接下来把Field给码上:

package field;
import java.util.ArrayList;
import java.util.List;
import ICell.ICell;
public class Field {
 private int height;
 private int width;
 private ICell[][] cells;
 /*
  * 1. adjacent为什么要将它设置为静态常量呢?
  */
 private static final Location[] adjacent = {
   new Location(-1, 1),new Location(0, 1),new Location(1, 1),
   new Location(-1, 0),new Location(0, 0),new Location(0, 1),
   new Location(-1, -1),new Location(0, -1),new Location(-1, -1)
 };
 public Field(int height, int width) {
  this.height = height;
  this.width = width;
  cells = new ICell[height][width];
 }
 public int getHeight() {
  return height;
 }
 public int getWidth() {
  return width;
 }
 public ICell get(int row, int col) {
  return cells[row][col];
 }
 public ICell place(int row, int col, ICell cell) {
  ICell ret = cells[row][col];
  cells[row][col] = cell;
  return ret;
 }
// public ICell[] getNeighbour(int row, int col) {
//  List<ICell> list = new ArrayList<ICell>();
//  for(int i = -1; i < 2; i ++) {
//   for(int j = -1; j < 2; j ++) {
//    int r = row + i;
//    int c = col + j;
//    if(r > -1 && r < height && c > -1 && c < width && !(r == row && c == col)) {
//     list.add(cells[r][c]);
//    }
//   }
//  }
//  return list.toArray(new ICell[list.size()]);
// }
 public ICell[] getNeighbour(int row, int col) {
  List<ICell> list = new ArrayList<ICell>();
  for(Location loc : adjacent) {
   int r = row + loc.getRow();
   int c = col + loc.getCol();
   if(r > -1 && r < height && c > -1 && c < width && !(r == row && c == col)) {
    list.add(cells[r][c]);
   }
  }
  return list.toArray(new ICell[list.size()]);
 }
 public Location[] getFreeAdj(int row, int col) {
  List<Location> list = new ArrayList<Location>();
  for(Location loc : adjacent) {
   int r = row + loc.getRow();
   int c = col + loc.getCol();
   if(r > -1 && r < height && c > -1 && c < width && (cells[r][c] == null)) {
    list.add(new Location(r, c));
   }
  }
  return list.toArray(new Location[list.size()]);
 }
 //给指定的对象在指定的位置附近随机放置;需要返回什么吗?不需要返回什么;
 public void placeRandomAdj(int row, int col, ICell cell) {
  Location[] loc = this.getFreeAdj(row, col);
  if(loc.length > 0) {
   int index = (int)(Math.random() * loc.length);
   cells[loc[index].getRow()][loc[index].getCol()] = cell;
  }
 }
 public ICell remove(int row, int col) {
  ICell ret = cells[row][col];
  cells[row][col] = null;
  return ret;
 }
 public void remove(ICell cell) {
  for(int row = 0; row < height; row ++) {
   for(int col = 0; col < width; col ++) {
    if(cells[row][col] == cell) {
     cells[row][col] = null;
     break;
    }
   }
  }
 }
 //将指定的对象移动到指定的地方
 public void move(int row, int col, Location loc) {
  cells[loc.getRow()][loc.getCol()] = cells[row][col];
  remove(row, col);
 }
}

现在是表现类View:

package field;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import ICell.ICell;
public class View extends JPanel {
 private static final int GIRD_SIZE = 16;
 private Field field;
 
 public View(Field field){
  this.field = field;
 }
 
 @Override
 public void paint(Graphics g) {
  super.paint(g);
  for(int row = 0; row < field.getHeight(); row ++) {
   g.drawLine(0, row * GIRD_SIZE, field.getWidth() * GIRD_SIZE, row * GIRD_SIZE);
  }
  for(int col = 0; col < field.getHeight(); col ++) {
   g.drawLine( col * GIRD_SIZE, 0, col * GIRD_SIZE, field.getHeight() * GIRD_SIZE);
  }
  for(int row = 0; row < field.getHeight(); row ++) {
   for(int col = 0; col < field.getWidth(); col ++) {
    ICell cell  = field.get(row, col);
    if(cell != null) {
     cell.draw(g, col, row, GIRD_SIZE);
    }
   }
  }
 }
 
 @Override
 public Dimension getPreferredSize() {
  return new Dimension(field.getWidth() * GIRD_SIZE + 1, field.getHeight() * GIRD_SIZE + 1);
 }
}

现在我们已经把整个程序中的功能类已经完成了,现在通过引入更高层次的类去实现它们之间的一个通信,这个,我们就将在下一篇当中去详细展开~

转载于:https://my.oschina.net/u/2559261/blog/591427

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值