gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java
package com.github.kuangcp.virusbroadcast.domain;
import com.github.kuangcp.virusbroadcast.Hospital;
import com.github.kuangcp.virusbroadcast.constant.Constants;
import com.github.kuangcp.virusbroadcast.constant.PersonState;
import com.github.kuangcp.virusbroadcast.gui.DisplayPanel;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
import static com.github.kuangcp.virusbroadcast.constant.Constants.SAFE_DISTANCE;
/**
*
*/
@Data
@Slf4j
public class Person {
private Bed bed;
private int x;
private int y;
private MoveTarget moveTarget;
private int state = PersonState.NORMAL;
private static Map<Integer, Consumer<Person>> functions = new HashMap<>();
int sig = 1;
/**
* 被感染时间
*/
int infectedTime = 0;
/**
* 确诊时间
*/
int confirmedTime = 0;
double targetXU;
double targetYU;
double targetSig = 50;
public Person(int x, int y) {
this.x = x;
this.y = y;
targetXU = 100 * ThreadLocalRandom.current().nextGaussian() + x;
targetYU = 100 * ThreadLocalRandom.current().nextGaussian() + y;
}
public boolean wantMove() {
double value = sig * ThreadLocalRandom.current().nextGaussian() + Constants.INTENTION;
return value > 0;
}
public boolean isInfected() {
return state >= PersonState.SHADOW && state != PersonState.DEAD;
}
/**
* 被感染
*/
public void beInfected() {
if (this.isInfected()) {
return;
}
City.trans(PersonState.NORMAL, PersonState.SHADOW);
state = PersonState.SHADOW;
infectedTime = DisplayPanel.worldTime;
}
public void confirmed() {
City.trans(PersonState.SHADOW, PersonState.CONFIRMED);
state = PersonState.CONFIRMED;
confirmedTime = DisplayPanel.worldTime;
}
public void willInfected() {
if (this.isInfected()) {
return;
}
// 是否能被其他人感染
List<Person> people = City.getInstance().personList;
for (Person person : people) {
if (person.getState() == PersonState.NORMAL) {
continue;
}
if (this.mayInfected(person)) {
this.beInfected();
}
}
}
/**
* 是否感染
*/
public boolean mayInfected(Person person) {
if (person.getState() == PersonState.NORMAL) {
return false;
}
float random = ThreadLocalRandom.current().nextFloat();
return random < Constants.BROAD_RATE && distance(person) < SAFE_DISTANCE;
}
public double distance(Person person) {
return Math.sqrt(Math.pow(x - person.getX(), 2) + Math.pow(y - person.getY(), 2));
}
private void moveTo(int x, int y) {
this.x += x;
this.y += y;
}
private boolean freeze(Bed bed) {
if (Objects.nonNull(this.bed) || Objects.isNull(bed)) {
// System.out.println("隔离区没有空床位");
return false;
}
City.trans(PersonState.CONFIRMED, PersonState.FREEZE);
this.bed = bed;
state = PersonState.FREEZE;
x = bed.getX();
y = bed.getY();
bed.setEmpty(false);
return true;
}
private boolean cure() {
City.trans(PersonState.FREEZE, PersonState.NORMAL);
if (Objects.isNull(this.bed)) {
return false;
}
this.bed.setEmpty(true);
this.bed = null;
this.state = PersonState.NORMAL;
return true;
}
private boolean dead() {
City.trans(this.state, PersonState.DEAD);
if (Objects.nonNull(this.bed)) {
this.bed.setEmpty(true);
this.bed = null;
}
this.state = PersonState.DEAD;
return true;
}
// 随机移动 TODO 确诊无法移动
private void action() {
if (state == PersonState.FREEZE) {
return;
}
if (!wantMove()) {
return;
}
if (moveTarget == null || moveTarget.isArrived()) {
double targetX = targetSig * ThreadLocalRandom.current().nextGaussian() + targetXU;
double targetY = targetSig * ThreadLocalRandom.current().nextGaussian() + targetYU;
moveTarget = new MoveTarget((int) targetX, (int) targetY);
}
int dX = moveTarget.getX() - x;
int dY = moveTarget.getY() - y;
double length = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
if (length < 1) {
moveTarget.setArrived(true);
return;
}
int udX = (int) (dX / length);
if (udX == 0 && dX != 0) {
if (dX > 0) {
udX = 1;
} else {
udX = -1;
}
}
int udY = (int) (dY / length);
if (x > 700) {
moveTarget = null;
if (udX > 0) {
udX = -udX;
}
}
this.moveTo(udX, udY);
}
public void update() {
functions.get(state).accept(this);
}
static {
functions.put(PersonState.NORMAL, p -> {
p.action();
p.willInfected();
});
functions.put(PersonState.SHADOW, p -> {
if (DisplayPanel.worldTime - p.infectedTime > Constants.SHADOW_TIME) {
p.confirmed();
}
p.action();
});
functions.put(PersonState.CONFIRMED, p -> {
if (DisplayPanel.worldTime - p.confirmedTime >= Constants.HOSPITAL_RECEIVE_TIME) {
Bed bed = Hospital.INSTANCE.pickBed();
p.freeze(bed);
} else {
p.action();
}
int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE);
if (rate <= Constants.DEAD_RATE) {
p.dead();
}
});
functions.put(PersonState.FREEZE, p -> {
// 看作数轴上两个线段
int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE);
if (rate <= Constants.CURE_RATE) {
p.cure();
} else if (rate <= Constants.DEAD_RATE + Constants.CURE_RATE) {
p.dead();
}
});
functions.put(PersonState.DEAD, p -> {
// log.info("dead: person={}", p);
City.getInstance().dead(p);
});
}
}