JavaFX в игре шашки


Я начал самостоятельно учить Джаву несколько месяцев назад. Недавно я пришел к точке, где я чувствовал, что это было время плотными себя от любой помощи или руководящих материалов, кроме документов в Oracle, поэтому я сделал 2 игрока игра в шахматы. Это очень простой. Я не включают в себя визуальный индикатор для коронации, только текстовое уведомление. Я хотел бы некоторую обратную связь на местах, которые нуждаются в улучшении, плохие привычки, советы, как. Это будет немного необычный пост, так как он охватывает несколько классов файлов.

Есть 2 упаковки, основная и главная.шашки. Я сокращу каждый файл блок класса С выше пути.

главная.Главная

package main;

import main.checkers.Entry;

public class Main {

    public static void main(String[] args) {
        Entry.main(null);
    }
}

главная.шашки.Шашка

package main.checkers;

import java.lang.Error;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.input.MouseEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.beans.binding.When;
import javafx.beans.binding.DoubleBinding;


@SuppressWarnings("restriction")
//make checker a child of checkerBoard so that it cannot exist independently
public class Checker extends Circle {


    private boolean king = false;
    public final String team;
    //for binding the checkers to the grid
    private DoubleBinding radiusBinding;

    Checker(CheckerBoard board, String tmp) {
        //decide if checker is red or black
        if(tmp.equals("red")) {
            team = tmp;
            setFill(Color.RED);
        }
        else if(tmp.equals("black")) {
            team = tmp;
            setFill(Color.BLACK);
        }
        else throw new Error("team must be red or black");
        //center piece
        board.setHalignment(this, HPos.CENTER);
        board.setValignment(this, VPos.CENTER);
        radiusBinding = (DoubleBinding)new When(board.cellWidthProperty().lessThan(board.cellHeightProperty()))
            .then(board.cellWidthProperty().multiply(.5*.8))
            .otherwise(board.cellHeightProperty().multiply(.5*.8)
        );
        //bind the radius of the checker to .8 * width of it's container
        radiusProperty().bind(radiusBinding);

        //add local event handlers to highlight and unhighlight the checker when dragged
        addEventHandler(MouseEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() {
            public void handle(MouseEvent e) {
                toFront();
                setStroke(Color.YELLOW);
                startFullDrag();
                transferEvent(e);
            }
        });
        addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
            public void handle(MouseEvent e) {
                setStroke(null);
                transferEvent(e);
            }
        });
        //register static eventHandler to forward control of the action
        addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
            public void handle(MouseEvent e) {
                transferEvent(e);
            }
        });
        //add change listener to team 
    }

    //convienence method to transfer event to checkerboard
    private <T extends MouseEvent> void transferEvent(T e) {
        //cheating to get dynamic reference within a static object
        CheckerBoard dynamicBoard = (CheckerBoard)((Checker)e.getSource()).getParent();
        dynamicBoard.transferControl(e);
    }

    //getters and setters for king state
    public boolean getKing() {
        return king;
    }
    public void setKing(boolean state) {
        king = state;
    }
}

главная.шашки.Шахматка

package main.checkers;

import javafx.scene.input.MouseEvent;
import javafx.beans.value.ChangeListener;
import javafx.beans.property.StringProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;


@SuppressWarnings("restriction")
public class CheckerBoard extends TrueGrid {

    //the controller object for this checkerboard
    private Controller maestro;
    //Keep track of the turn and alert controller when it changes
    private StringProperty turn;

    //factory method to create checkerboard object
    public static CheckerBoard createCheckerBoard() {
        CheckerBoard ret = new CheckerBoard();
        ret.setTiles();
        ret.setBoard();
        ret.setController(new Controller(ret));
        ret.getController().setValidSelection();
        return ret;
    }
    //create an 8x8 Grid 
    //wrap in a factory method
    private CheckerBoard() {
        super(8, 8);
        //set the property, add change listener
        turn = new SimpleStringProperty("red");
        turn.addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> property, String oldVal, String newVal) {

            }
        });
    }
    //set the board 
    private void setBoard() {
        String team = "red";
        //Only initiailze the first and last three rows with checkers
        for(int j=0; j<8; j++){
            if(j > 2 && j <5) {
                team = "black";
                continue;
            }
            for(int i=0; i<8; i+=2) {
                if(j%2 == 0) add(new Checker(this, team), i+1, j);
                else add(new Checker(this, team), i, j);
            }
        }
    }
    //set the tiles
    private void setTiles() {
        for(int j=0; j<8; j++) {
            for(int i=0; i<8; i++) {
                Rectangle tmp = new Rectangle();
                tmp.widthProperty().bind(cellWidthProperty());
                tmp.heightProperty().bind(cellHeightProperty());
                if(j%2 == 0) {
                    if(i%2 == 0) {
                        tmp.setFill(Color.MOCCASIN);
                        add(tmp, i, j);
                    }
                    else {
                        tmp.setFill(Color.MAROON);
                        add(tmp, i, j);
                    }
                }
                else {
                    if(i%2 == 0) {
                        tmp.setFill(Color.MAROON);
                        add(tmp, i, j);
                    }
                    else {
                        tmp.setFill(Color.MOCCASIN);
                        add(tmp, i, j);
                    }
                }
            }
        }
    }
    //transfers control of the event to the controller for this game of checkers
    public <T extends MouseEvent> void transferControl(T e) {
        maestro.recieveControl(e);      
    }
    //getters and setters for turn property
    public StringProperty getTurnProperty() {
        return turn;
    }
    public String getTurn() {
        return turn.getValue();
    }
    public Controller getController() {
        return maestro;
    }
    public void setController(Controller tmp) {
        maestro = tmp;
    }
}

главная.шашки.Контроллер

package main.checkers;

import java.util.List;
import java.util.ArrayList;
import javafx.scene.input.MouseEvent;
import java.util.function.Predicate;
import javafx.scene.Node;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;


@SuppressWarnings("restriction")
public class Controller {

    //the board which this controller is bound to
    private final CheckerBoard board;
    //a list of all valid moves that can be made
    private List<PointCB> absoluteValid = new ArrayList<PointCB>();
    //is the currently selected checker a valid piece to be moved?
    private boolean validSelection;
    //a global refernce to the last checker marked to be a valid movement as well it's previous coordinates
    private Checker cleared;
    private int prevI, prevJ;
    private List<PointCB> relativeValid;

    Controller(CheckerBoard tmp) {
        board = tmp;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public void setValidSelection() {
        //clear old list
        absoluteValid.clear();
        //determine which pieces to scan
        List<Checker> team;
        Predicate<Node> filterCondition = (x) -> {
            if(x.getClass() == Checker.class) {
                return ((Checker)x).team.equals(board.getTurn());
            }
            else return false;
        };
        team = (List)(board.getChildren().filtered(filterCondition));
        //scan third row viability of each piece
        for(Checker checker : team) {
            absoluteValid.addAll(scanR2(checker));
        }
        //if no jumps are avaible then the list of valid movements it the list of single jump spaces availbe
        if(absoluteValid.isEmpty()) {
            for(Checker checker : team) {
                absoluteValid.addAll(scanR1(checker));
            }
        }
    }

    //scans ring 1 of adjacent grid cells for valid movements
    public List<PointCB>  scanR1(Checker checker) {
        //get coordinates of checker in grid
        int i = board.getColumnIndex(checker);
        int j = board.getRowIndex(checker);
        List<PointCB> ret = new ArrayList<PointCB>();
        //scanner loop ... y axis 
        for(int dY=-1; dY<2; dY+=2) {
            //if checker is not a checker.getKing() only scan subjectively forward
            if(!checker.getKing()) {
                if(checker.team.equals("red") && dY==-1) continue;
                else if(checker.team.equals("black") && dY==1) continue;
            }
            if(j+dY<0) continue;
            else if(j+dY>7) break;
            //x-axis loop
            for(int dX=-1; dX<2; dX+=2) {
                //add empty spaces to list
                if(board.getCell(Checker.class, i+dX, j+dY).isEmpty()) ret.add(new PointCB(checker, i+dX, j+dY));
            }
        }
        return ret;
    }
    //scans ring 3 of adjacent grid cells for valid movements
    public List<PointCB>  scanR2(Checker checker) {
        //get coordinates of checker in grid
        int i = board.getColumnIndex(checker).intValue();
        int j = board.getRowIndex(checker).intValue();
        List<PointCB> ret = new ArrayList<PointCB>();
        String searchFor = checker.team.equals("red") ? "black" : "red";
        //scanner loop ... y axis 
        for(int dY=-2; dY<3; dY+=4) {
            //if checker is not a checker.getKing() only scan subjectively forward
            if(!checker.getKing()) {
                if(checker.team.equals("red") && dY==-2) continue;
                else if(checker.team.equals("black") && dY==2) continue;
            }
            if(j+dY<0) continue;
            else if(j+dY>7) break;
            //x-axis loop
            for(int dX=-2; dX<3; dX+=4) {

                if(i+dX<0) continue;
                else if(i+dX>7) break;
                //add empty spaces to list
                if(!board.getCell(Checker.class, i+dX/2, j+dY/2).isEmpty()) {
                    if(board.getCell(Checker.class, i+dX/2, j+dY/2).get(0).team.equals(searchFor)) {
                        if(board.getCell(Checker.class, i+dX, j+dY).isEmpty()) ret.add(new PointCB(checker, i+dX, j+dY));
                    }
                }
            }
        }
        return ret;
    }

    public <T extends MouseEvent>void recieveControl(T e) {
        //Save event type as String for logical testing
        String eventType = e.getEventType().getName();
        //forward the event to the correct method for final processing
        switch(eventType) {

            case "DRAG_DETECTED" : controlDragDetected(e);
                break;
            case "MOUSE_DRAGGED" : controlMouseDragged(e);
                break;
            case "MOUSE_RELEASED" : controlMouseReleased(e);
                break;
        }
    }
    public List<PointCB> getRelativeValid(Checker checker) {
        //list for holding return value
        List<PointCB> ret = new ArrayList<PointCB>();
        //add all available jumps to list
        ret.addAll(scanR2(checker));
        //if list is empty add all available adjacent spaces
        if(ret.isEmpty()) ret.addAll(scanR1(checker));
        return ret;
    }
/********************************************************************************************
 * ******************************************************************************************
 *              DRAG CONTROLS
 * ******************************************************************************************
 * ******************************************************************************************/
    //PROBLEM 
    public void controlDragDetected(MouseEvent e) {
        //get relative valid movements for comparison
        Checker checker = (Checker)e.getSource();
        int i = board.getColumnIndex(checker);
        int j = board.getRowIndex(checker);
        relativeValid = getRelativeValid(checker);
        List<PointCB> tmp = new ArrayList<PointCB>();
        //compare relative vlaid movements to absolute get the true list of relatively valid movements
        for(PointCB local : relativeValid) {
            for(PointCB remote : absoluteValid) {
                if(remote.equals(local)) tmp.add(local);
            }
        }
        relativeValid = tmp;
        //if there are valid moves relative to the selected piece, enable it to move freely
        if(!relativeValid.isEmpty()) {
            for(PointCB p : relativeValid) {
                board.getCell(Rectangle.class, p).forEach((x) -> x.setFill(Color.CHARTREUSE));
            }
            checker.setManaged(false);
            //mark this as being the last cleared piece
            cleared = checker;
            prevI = i;
            prevJ = j;
        }
        else {
            board.getCell(Rectangle.class, i, j).forEach((x) -> x.setFill(Color.CRIMSON));
        }
    }
    //if the piece has been cleared as valid allow it to be moved
    public void controlMouseDragged(MouseEvent e) {
        Checker checker = (Checker)e.getSource();
        if(checker == cleared) {
            checker.setCenterX(e.getX());
            checker.setCenterY(e.getY());
        }
        else e.consume();
    }
    //when the mouse is released, place it according to wheather it was relased into a valid cell
    public void controlMouseReleased(MouseEvent e) {
        Checker checker = (Checker)e.getSource();
        if(checker == cleared) {
            boolean set = false;
            int i = (int)(e.getSceneX()/board.cellWidth());
            int j = (int)(e.getSceneY()/board.cellHeight());
            //check if the drop location is valid
            PointCB currentCoord = new PointCB(checker, i, j);
            for(PointCB p : relativeValid) {
                if(currentCoord.equals(p)) {
                    board.getChildren().remove(checker);
                    board.add(checker, i, j);
                    set = true;
                }
            }
            //reset the board to it's default coloring
            if(!set) {
                board.getChildren().remove(checker);
                board.add(checker, prevI, prevJ);
            }
            checker.setManaged(true);
            if(Math.abs(board.getColumnIndex(checker)-prevI)==2 && Math.abs(board.getRowIndex(checker)-prevJ)==2) {
                handleJump(checker);
            }
            else if( !(board.getColumnIndex(checker)==prevI || board.getRowIndex(checker)==prevJ)) changeTurn();
            checkCrown();
        }
        resetTiles();
    }
    /*******************************************************************************************
     *                END CONTROLS
     *******************************************************************************************/
    //reset the tiles to their default colors
    @SuppressWarnings({"rawtypes", "unchecked"})
    public void resetTiles() {
        List<Rectangle> tmp = (List)board.getChildren().filtered((x) ->{
            //only maroon pieces can be valid movements therefore any piece that is not moccasin should be maroon
            if(x.getClass() == Rectangle.class) {
                if(!((Rectangle)x).getFill().equals(Color.MOCCASIN)) return true;
                else return false;
            }
            else return false;
        });
        for(Rectangle r : tmp) r.setFill(Color.MAROON);
    }
    //if a jump is detected, determine if further jumps need be executed
    public void handleJump(Checker checker) {
        //deal with a jump event
        int dX = board.getColumnIndex(checker)-prevI;
        int dY = board.getRowIndex(checker)-prevJ;
        //remove the piece that was jumped over
        board.getChildren().removeAll(board.getCell(Checker.class, (prevI+dX/2), (prevJ+dY/2)));
        //clear previous valid selection
        //limit new valid selection to this piece and only jumps
        absoluteValid.clear();
        absoluteValid.addAll(scanR2(checker));
        if(absoluteValid.isEmpty()) changeTurn();
    }
    //change the turn, progress the game
    public void changeTurn() {
        String setTo = board.getTurn().equals("red") ? "black" : "red";
        board.getTurnProperty().setValue(setTo);
        setValidSelection();
    }
    //crown the piece if need be
    public void checkCrown() {
        int kingColumn = cleared.team.equals("red") ? 7 : 0;
        int j = board.getRowIndex(cleared);
        if(j == kingColumn) {
            cleared.setKing(true);
            System.out.println("PIECE KINGED SUCCESFULLY");
        }
    }
}

главная.шашки.Запись

package main.checkers;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;


@SuppressWarnings("restriction")
public class Entry extends Application {

    //create window, add checkerboard, start game loop
    public void start(Stage stage) {
        CheckerBoard board = CheckerBoard.createCheckerBoard();
        Scene scene = new Scene(board, 500, 500);
        board.bindToParent();
        stage.setScene(scene);
        board.setGridLinesVisible(true);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

главная.шашки.PointCB

package main.checkers;

import javafx.geometry.Point2D;

//A Point2D linked specific checker 
@SuppressWarnings("restriction")
public class PointCB extends Point2D {
    public final Checker checker;
    PointCB(Checker tmp, int x, int y) {
        super(x, y);
        checker = tmp;
    }
    public boolean equals(Object o) {
        if(o.getClass() == PointCB.class) {


            return super.equals(o) && ((PointCB)(o)).checker == checker;
        }
        else return false;
    }
}

главная.шашки.TrueGrid

package main.checkers;

import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.ColumnConstraints;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.DoubleProperty;
import javafx.scene.Node;
import java.util.List;
import java.util.ArrayList;
import javafx.geometry.Point2D;


@SuppressWarnings("restriction")
public class TrueGrid extends GridPane{

    //properties
    private DoubleProperty cellWidth = new SimpleDoubleProperty();
    private DoubleProperty cellHeight = new SimpleDoubleProperty();
    public final int rows;
    public final int columns;

    //constructor
    public TrueGrid(int i, int j) {
        columns = i;
        rows = j;
        initializeColumns(i);
        initializeRows(j);
    }
    //fill the grid with i columns
    private void initializeColumns(int i) {
        for(int c=0; c<i; c++){
            getColumnConstraints().add(new ColumnConstraints());
            getColumnConstraints().get(c).setPercentWidth(100.0/i);
        }
    }
    //fill the grid with j rows
    private void initializeRows(int j) {
        for(int c=0; c<j; c++){
            getRowConstraints().add(new RowConstraints());
            getRowConstraints().get(c).setPercentHeight(100.0/j);
        }
    }
    //bind cell width and height properties to their value within the scene
    public void bindToParent() throws NullPointerException {
        if(getScene() == null) throw new NullPointerException("scene value null! does this TrueGrid belong to a scene?");
        cellWidth.bind(getScene().widthProperty().divide(columns));
        cellHeight.bind(getScene().heightProperty().divide(rows));
    }
    //getter methods
    public DoubleProperty cellWidthProperty() {
        return cellWidth;
    }
    public double cellWidth() {
        return cellWidth.getValue();
    }
    public DoubleProperty cellHeightProperty() {
        return cellHeight;
    }
    public double cellHeight() {
        return cellHeight.getValue();
    }
    //get the child contained at (i,j) coordinate of this grid
    //if T is null no children will be filtered
    public <T extends Node> List<T> getCell(Class<T> conversion, int iIndex, int jIndex) {
        List<T> children = new ArrayList<T>();
        for(Node node : getChildren()) {
            if(conversion.isInstance(node)) {   
                if(getColumnIndex(node) == iIndex) {
                    if(getRowIndex(node) == jIndex) {
                        children.add(conversion.cast(node)); 
                    }
                }
            }
        }
        return children;
    }
    //a wrapper class to allow passing Point2D instead of int coordinates to getCell
    public <T extends Node> List<T> getCell(Class<T> conversion, Point2D p) {
        return getCell(conversion, (int)p.getX(), (int)p.getY());
    }
    //remove all children of a given node type at the coordinates
    public <T extends Node> void removeCell(Class<T> clazz, int i, int j) {
        getChildren().removeAll(getCell(clazz, i, j));
    }

}


915
5
задан 20 февраля 2018 в 04:02 Источник Поделиться
Комментарии
1 ответ

У меня нет проблемы языка, но ваши комментарии (в коде) покажутся лишними, мне. Как я говорю, когда я комментарий на VBA


Комментарии - "код сказать вам, как, комментарии, скажу вам
почему"
.
Код должен говорить сам за себя, если он нуждается в комментариях, оно может понадобиться
чтобы быть более ясным. Если нет, то комментарий должен описывать почему
ты что-то делаешь, а не как вы делаете это (или что это). Здесь
несколько причиндля
избегайте замечаний все вместе.

например


//properties
private DoubleProperty cellWidth = new SimpleDoubleProperty();
private DoubleProperty cellHeight = new SimpleDoubleProperty();
public final int rows;
public final int columns;

Они называют свойства в коде. Я чувствую суматоху просто смотрел на нее.

2
ответ дан 21 февраля 2018 в 01:02 Источник Поделиться