gridworld のケース スタディの GUI を実装しようとしていて、行き詰まりました。カスタマイズされた JTable を使用してグリッドを実装しようとしています。TreeMap を使用して BoundedGrid および UnboundedGrid クラスを実装しました。ただし、UnboundedGrid クラスでは、GUI で行と列の数を動的に変更する必要があります。つまり、Grid に配置されている要素の位置に基づいています。JTable の行と列の数は、最大および最小の x/y 座標を持つ要素によって決定される必要があります。AbstractTableModel クラスを正しく拡張する方法がわかりません。
さらに、Grid に格納されたオブジェクト、Actors は、それらのコンテナー Grid とそれぞれの場所を格納し、Grid インターフェイスの put メソッドと remove メソッドは Actor クラスで使用されるため、Actors の変更は Actor クラスの対応するメソッドを呼び出す必要があります。たとえば、Actor の 1 つのメソッドは次のとおりです。
public void putSelfInGrid(Grid<Actor> grid, Location loc){
if (this.grid != null){
throw new IllegalStateException("This actor: " + this + " is already contained in another grid.");
}
Actor actor = grid.get(loc);
if (actor != null) {
actor.removeSelfFromGrid();
}
grid.put(loc, this);
this.grid = grid;
this.location = loc;
}
このメソッドは、Grid インターフェイスの put() メソッドを呼び出し、さらに Actor オブジェクトの場所を更新します。グリッドの変更を更新するために、グリッドとアクターの両方を格納する GridManager のようなクラスが必要なので、これを考慮する必要があります。別の質問: GridManager は JTable を直接拡張する必要がありますか、または JTable を拡張し、GridManager を使用してテーブルを自動的に更新する GridTable などの別のクラスを作成する必要があります。
これが私のコードです:
https://www.dropbox.com/sh/a6qku7pphrihtf5/AABxOypOCxKlS05i-AeMpBOZa?dl=0
public interface Grid<E> {
E put(Location loc, E obj);
E remove(Location loc);
E get(Location loc);
int getNumRows();
int getNumCols();
boolean isValid(Location loc);
default boolean isEmpty(Location loc){...}
default Set<Location> getValidAdjacentLocations(Location loc){...}
default Optional<Location> getValidAdjacentLocationToward(Location loc, Location.Direction direction){...}
default Set<Location> getEmptyAdjacentLocations(Location loc){...}
default Optional<Location> getEmptyAdjacentLocationToward(Location loc, Location.Direction direction){...}
default Set<Location> getOccupiedAdjacentLocations(Location loc){...}
default Optional<Location> getOccupiedAdjacentLocationToward(Location loc, Location.Direction direction){...}
default Set<E> getNeighbors(Location loc){...}
Set<Location> getOccupiedLocations();
default Set<E> getAllElements(){...}
}
public abstract class AbstractGrid<E> implements Grid<E>{
private final int rowNum;
private final int colNum;
protected AbstractGrid(){
this(-1, -1);
};
protected AbstractGrid(int rowNum, int colNum){
if (rowNum < -1 || colNum < -1){
throw new IllegalArgumentException("Invalid Dimension");
}
this.rowNum = rowNum;
this.colNum = colNum;
}
protected AbstractGrid(Dimension dimension){
this(dimension.height, dimension.width);
}
@Override
public int getNumRows() {
return rowNum;
}
@Override
public int getNumCols() {
return colNum;
}
//toString, equals and hashCode ...
}
public class Location implements Comparable<Location>{
public static enum Direction{
NORTH(0),
NORTHEAST(45),
EAST(90),
SOUTHEAST(135),
SOUTH(180),
SOUTHWEST(225),
WEST(270),
NORTHWEST(315),
;
private final int value;
private Direction(int value){
this.value = value;
}
public int getValue() {
return value;
}
public Direction turn(Turning turning){
int newDegree = Math.floorMod(getValue() + turning.getValue(), 360);
for (Direction direction : Direction.values()) {
if (direction.getValue() == newDegree) {
return direction;
}
}
throw new UnknownError("missing direction values");
}
public int getColOffset(){
if ((getValue() == 0 || getValue() == 180)) {
return 0;
} else if (0 < getValue() && getValue() < 180){
return 1;
} else if (180 < getValue() && getValue() < 360){
return -1;
} else{
throw new UnknownError("unexpected value");
}
}
public int getRowOffset(){
if (getValue() == 90 || getValue() == 270){
return 0;
} else if (90 < getValue() && getValue() < 270){
return 1;
} else if ((0 <= getValue() && getValue() < 90) || (270 < getValue() && getValue() < 360)){
return -1;
} else {
throw new UnknownError("unexpected value");
}
}
}
public static enum Turning{
AHEAD(0),
HALF_RIGHT(45),
HALF_LEFT(-45),
RIGHT(90),
LEFT(-90),
HALF_CIRCLE(180),
CIRCLE(360),
;
private final int value;
private Turning(int value){
this.value = value;
}
public int getValue() {
return value;
}
}
private final int row;
private final int col;
public Location(int row, int col){
this.row = row;
this.col = col;
}
public int getCol() {
return col;
}
public int getRow() {
return row;
}
public Location getAdjacentLocation(Direction direction){...}
public Set<Location> getAdjacentLocations(){...}
public Direction getDirectionToward(Location target){...}
@Override
public int compareTo(Location o) {
if (this.row != o.row){
return (int)Math.signum(Integer.compare(this.row, o.row));
} else {
return (int)Math.signum(Integer.compare(this.col, o.col));
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Location)) return false;
Location location = (Location) o;
if (col != location.col) return false;
if (row != location.row) return false;
return true;
}
//hashCode and toString
}
public class BoundedGrid<E> extends AbstractGrid<E>{
private Map<Location, E> occupants;
public BoundedGrid(int rowNum, int colNum) {
super(rowNum, colNum);
occupants = new TreeMap<>();
}
@Override
public E put(Location loc, E obj) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.put(loc, obj);
}
@Override
public E remove(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.remove(loc);
}
@Override
public E get(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.get(loc);
}
@Override
public boolean isValid(Location loc) {
return 0 <= loc.getRow() && loc.getRow() < getNumRows() && 0 <= loc.getCol() && loc.getCol() < getNumCols();
}
@Override
public Set<Location> getOccupiedLocations() {
return occupants.keySet();
}
}
public class UnboundedGrid<E> extends AbstractGrid<E>{
public static enum Characteristics{
HORIZONTALLY_BOUNDED, VERTICALLY_BOUNDED, UNBOUNDED;
}
private Characteristics characteristics;
private Map<Location, E> occupants;
public UnboundedGrid(){
super();
characteristics = Characteristics.UNBOUNDED;
occupants = new TreeMap<>();
}
public UnboundedGrid(Characteristics characteristics, int num){
super(characteristics == Characteristics.HORIZONTALLY_BOUNDED ? new Dimension(num, -1) : (characteristics == Characteristics.VERTICALLY_BOUNDED ? new Dimension(-1, num) : new Dimension(-1, -1)));
this.characteristics = characteristics;
occupants = new TreeMap<>();
}
@Override
public E put(Location loc, E obj) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.put(loc, obj);
}
@Override
public E remove(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.remove(loc);
}
@Override
public E get(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.get(loc);
}
@Override
public boolean isValid(Location loc) {
switch (characteristics){
case HORIZONTALLY_BOUNDED:
return 0 <= loc.getCol() && loc.getCol() < getNumCols();
case VERTICALLY_BOUNDED:
return 0 <= loc.getRow() && loc.getRow() < getNumRows();
case UNBOUNDED:
return true;
default:
throw new UnknownError("Check UnboundedGrid.Characteristics");
}
}
@Override
public Set<Location> getOccupiedLocations() {
return occupants.keySet();
}
}