Дизайн шахматной игры с использованием объектно-ориентированных принципов

Я хотел бы знать, правильный ли мой подход и как его можно улучшить? Кроме того, есть ли способ избавиться от отношения между Piece и Board? В настоящий момент я сохраняю положение предмета как в куске, так и на доске. Есть ли способ изменить это?

Я рассмотрел Game, чтобы содержать экземпляр Board и два Players (один черный, один белый). Части содержат соединение с Советом, потому что для того, чтобы определить, являются ли они действительными, нам нужно знать отношение к другим частям.

Могу ли я использовать шаблоны проектирования для этого? Должен ли я использовать интерфейсы вместо суперкласса?

Game.java

public class Game {
    private Board board = new Board();
    private Player white;
    private Player black;
    public Game() {
        super();
    }

    public void setColorWhite(Player player) {
        this.white = player;
    }

    public void setColorBlack(Player player) {
        this.black = player;
    }

    public Board getBoard() {
        return board;
    }

    public void setBoard(Board board) {
        this.board = board;
    }

    public Player getWhite() {
        return white;
    }

    public void setWhite(Player white) {
        this.white = white;
    }

    public Player getBlack() {
        return black;
    }

    public void setBlack(Player black) {
        this.black = black;
    }

    public boolean initializeBoardGivenPlayers() {
        if(this.black == null || this.white == null)
            return false;
        this.board = new Board();
        for(int i=0; i<black.getPieces().size(); i++){
            board.getSpot(black.getPieces().get(i).getX(), black.getPieces().get(i).getY()).occupySpot(black.getPieces().get(i));
        }
        return true;
    }

}

Player.java

public class Player {
    public final int PAWNS = 8;
    public final int BISHOPS = 2;
    public final int ROOKS = 2;
    public boolean white;

    private List<Piece> pieces = new ArrayList<>();

    public Player(boolean white) {
        super();
        this.white = white;
    }

    public List<Piece> getPieces() {
        return pieces;
    }


    public void initializePieces(){
        if(this.white == true){
            for(int i=0; i<PAWNS; i++){ // draw pawns
                pieces.add(new Pawn(true,i,2));
            }
            pieces.add(new Rook(true, 0, 0));
            pieces.add(new Rook(true, 7, 0));
            pieces.add(new Bishop(true, 2, 0));
            pieces.add(new Bishop(true, 5, 0));
            pieces.add(new Knight(true, 1, 0));
            pieces.add(new Knight(true, 6, 0));
            pieces.add(new Queen(true, 3, 0));
            pieces.add(new King(true, 4, 0));
        }
        else{
            for(int i=0; i<PAWNS; i++){ // draw pawns
                pieces.add(new Pawn(true,i,6));
            }
            pieces.add(new Rook(true, 0, 7));
            pieces.add(new Rook(true, 7, 7));
            pieces.add(new Bishop(true, 2, 7));
            pieces.add(new Bishop(true, 5, 7));
            pieces.add(new Knight(true, 1, 7));
            pieces.add(new Knight(true, 6, 7));
            pieces.add(new Queen(true, 3, 7));
            pieces.add(new King(true, 4, 7));
        }

    }
}

Board.java

public class Board {
    private Spot[][] spots = new Spot[8][8];

    public Board() {
        super();
        for(int i=0; i<spots.length; i++){
            for(int j=0; j<spots.length; j++){
                this.spots[i][j] = new Spot(i, j);
            }
        }
    }

    public Spot getSpot(int x, int y) {
        return spots[x][y];
    }

}

Spot.java

public class Spot {
    int x;
    int y;
    Piece piece;

    public Spot(int x, int y) {
        super();
        this.x = x;
        this.y = y;
        piece = null;
    }

    public void occupySpot(Piece piece){
        //if piece already here, delete it, i. e. set it dead
        if(this.piece != null)
            this.piece.setAvailable(false);
        //place piece here
        this.piece = piece;
    }

    public boolean isOccupied() {
        if(piece != null)
            return true;
        return false;
    }

    public Piece releaseSpot() {
        Piece releasedPiece = this.piece;
        this.piece = null;
        return releasedPiece;
    }

}

Piece.java

public class Piece {
    private boolean available;
    private int x;
    private int y;

    public Piece(boolean available, int x, int y) {
        super();
        this.available = available;
        this.x = x;
        this.y = y;
    }


    public boolean isAvailable() {
        return available;
    }
    public void setAvailable(boolean available) {
        this.available = available;
    }
    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
    public int getY() {
        return y;
    }
    public void setY(int y) {
        this.y = y;
    }

    public boolean isValid(Board board, int fromX, int fromY, int toX, int toY){
        if(toX == fromX && toY == fromY)
            return false; //cannot move nothing
        if(toX < 0 || toX > 7 || fromX < 0 || fromX > 7 || toY < 0 || toY > 7 || fromY <0 || fromY > 7)
            return false;
        return true;
    }

}

King.java

public class King extends Piece{

    public King(boolean available, int x, int y) {
        super(available, x, y);
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean isValid(Board board, int fromX, int fromY, int toX, int toY) {
        if(super.isValid(board, fromX, fromY, toX, toY) == false)
            return false;
        if(Math.sqrt(Math.pow(Math.abs((toX - fromX)),2)) + Math.pow(Math.abs((toY - fromY)), 2) != Math.sqrt(2)){
            return false;
        }
        return false;
    }

}

Knight.java

public class Knight extends Piece{

    public Knight(boolean available, int x, int y) {
        super(available, x, y);
    }

    @Override
    public boolean isValid(Board board, int fromX, int fromY, int toX, int toY) {
        if(super.isValid(board, fromX, fromY, toX, toY) == false)
            return false;

        if(toX != fromX - 1 && toX != fromX + 1 && toX != fromX + 2 && toX != fromX - 2)
            return false;
        if(toY != fromY - 2 && toY != fromY + 2 && toY != fromY - 1 && toY != fromY + 1)
            return false;

        return true;
    }

}

Bishop.java

public class Bishop extends Piece{

    public Bishop(boolean available, int x, int y) {
        super(available, x, y);
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean isValid(Board board, int fromX, int fromY, int toX, int toY) {
        if(super.isValid(board, fromX, fromY, toX, toY) == false)
            return false;

        if(toX - fromX == toY - fromY)
            return true;

        return false;
    }

}

Rook.java

public class Rook extends Piece{

    public Rook(boolean available, int x, int y) {
        super(available, x, y);
        // TODO Auto-generated constructor stub
    }


    @Override
    public boolean isValid(Board board, int fromX, int fromY, int toX, int toY) {
        if(super.isValid(board, fromX, fromY, toX, toY) == false)
            return false;
        if(toX == fromX)
            return true;
        if(toY == fromY)
            return true;
        return false;
    }
}

Queen.java

public class Queen extends Piece{

    public Queen(boolean available, int x, int y) {
        super(available, x, y);
    }

    @Override
    public boolean isValid(Board board, int fromX, int fromY, int toX, int toY) {
        if(super.isValid(board, fromX, fromY, toX, toY) == false)
            return false;
        //diagonal
        if(toX - fromX == toY - fromY)
            return true;
        if(toX == fromX)
            return true;
        if(toY == fromY)
            return true;

        return false;
    }

}
55 голосов | спросил SummerCode 5 FriEurope/Moscow2014-12-05T21:05:30+03:00Europe/Moscow12bEurope/MoscowFri, 05 Dec 2014 21:05:30 +0300 2014, 21:05:30

6 ответов


24

Не предлагая глубокий обзор кода (поскольку у меня не так много специфических знаний Java), давайте посмотрим, что полный «ход» влечет за собой в шахматы:

  • Игрок выбирает кусок для перемещения.
  • Piece делает юридический ход в соответствии со своими правилами перемещения.
  • В дополнение к правилам, основанным исключительно на движении, существует также логика захвата, поэтому епископ не может двигаться от a1-h8, если есть часть, сидящая на c3.
  • Если игрок был ранее проверен, и этот ход не удаляет чек, он должен быть отменен.
  • Если перемещение выставляет проверку, оно должно быть отменено /запрещено.
  • Если игрок захватывает кусок, удалите кусок (включая en passant!).
  • Если кусок является пешкой, достигающей заднего ранга, продвигайте ее.
  • Если этот ход является рокировкой, установите соответствующее положение ладьи соответственно. Но король и ладья могут только запираться, если они не двигались, поэтому вам нужно следить за этим. И если король движется через чек в замок, это тоже запрещено.
  • Если ход приводит к тупику или матчу, игра заканчивается.

Может быть более четное (?). Это сложный шаг, больше, чем просто подсчет и последующее занятие пробелов.

Поэтому моей общей интуицией было бы просто позвонить:

Game.move(currentSpot, NewSpot);

И метод move будет содержать весь код для проверки шагов выше:

  • Проверить Piece.isValidMove(currentSpot, newSpot); - возможно, здесь нужна логика роллинга, так как король перемещает более 1 места, а лад прыгает с короля)
  • Проверьте Player.isChecked() (это просто сахар для Player.Pieces["King"].CanBeCaptured() - более интересная логика здесь!) li>
  • Проверьте, содержит ли newSpot кусок, и если да, newSpot.Piece.Remove();
  • Постройте некоторую логику для вызова Piece.CheckEnPassant() (Piece заложена, первый ход, 2 шага, мимо вражеской пешки, которые переехали в позицию захвата на предыдущем ходу - получайте удовольствие от этого!)
  • Piece.CheckPromote() (Piece закладывается, перемещается на задний план противоположного игрока)
  • Проверьте, есть ли Game.isOver(), который проверяет Game.isStaleMate() и Game.isCheckMate().

Ваш класс Board очень анемичен, вы используете его только в своем коде как прокси-объект для массива пятен. Вы могли бы просто создать Совет как массив Spots in Game. В любом случае вы уже можете удалить его из своей логики, поскольку вся ваша логика полностью основана на Xs и Ys, которые вы проходите.

UPDATE

Я удалю все ваши свойства позиции из куска. Вы используете его только как прокси, чтобы выяснить, какое место занимает кусок во время инициализации. Вместо этого удалите Player.initializePieces() и просто инициализируйте доску с помощью частей в нужном месте (Board.Spot.Piece = King и т. Д.), А затем пусть игроки выбирают цвет.

ответил Kyle Hale 5 FriEurope/Moscow2014-12-05T22:01:38+03:00Europe/Moscow12bEurope/MoscowFri, 05 Dec 2014 22:01:38 +0300 2014, 22:01:38
27

Мои комментарии касаются дизайна игры. Я вижу, что обязанности сущностей смешаны во многих местах.

  • Игрок не должен инициализировать фрагменты. Мы можем перенести ответственность на борт. Совету и кускам не нужно знать о игроках.
  • Части не должны обрабатывать перемещение. Pieces может предоставить список возможных шагов для достижения пути назначения, но на плате должен быть выбран допустимый путь.
  • Совет должен проверить наличие условия «Проверить соответствие».
  • Игра должна отслеживать перемещение игроков в историю и выбор цвета куска.
  • Класс игрока должен содержать только данные игрока.

Ниже приведена диаграмма грубого класса введите описание изображения здесь>> </p></body></html>

ответил Ramanathan Ganesan 19 AMpSun, 19 Apr 2015 06:04:14 +030004Sunday 2015, 06:04:14
19

Некоторые быстрые снимки

  • a if, написанное как if (booleanVariable==true) можно упростить до if (booleanVariable)
  • у вас не должно быть общедоступных переменных типа public boolean white;
  • нет конструктора Game, Board, Player и Piece должен вызывать super(), потому что они, очевидно, не наследуют /не расширяют какой-либо класс.

Некоторые быстрые кадры дизайна

  • шахматной игре нужна доска, 2 игрока и 32 штуки.
  • части являются частью Правления.
  • Игрок перемещает кусок по правилам
  • правила привязаны к типу детали и расположению фигур на доске.
  • эти правила должны оцениваться каким-либо объектом, либо Game, либо RuleEvaluator.
ответил Heslacher 5 FriEurope/Moscow2014-12-05T21:50:06+03:00Europe/Moscow12bEurope/MoscowFri, 05 Dec 2014 21:50:06 +0300 2014, 21:50:06
10

Вместо использования логического значения для флага, если что-то есть (и, следовательно, подразумевает, что это не другое), рассмотрите enum; в этом случае вы ничего не покупаете с точки зрения гибкости (поскольку это не так, как если бы это было когда-либо, скажем, красное или фиолетовое или что-то еще), но это сделает ваш код более понятным.

В игре вместо того, чтобы устанавливать черно-белый отдельно, у вас должен быть один setPlayers(Player black, Player white), или еще лучше, чтобы плата была фабрикой, которая обеспечивает черный и белый Player s - если есть даже причина иметь объект Player в первую очередь.

ответил fluffy 6 SatEurope/Moscow2014-12-06T00:51:12+03:00Europe/Moscow12bEurope/MoscowSat, 06 Dec 2014 00:51:12 +0300 2014, 00:51:12
5

Пара дополнений ко всем остальным:

  • оба Player и Board должны знать, где находятся части, как их, так и противники. Не могу подумать, что лучший способ разложить это, но подумайте, как они будут разговаривать друг с другом с минимальным дублированием. Я думаю, что имеет смысл поставить его под Board, а затем Player сохранить ссылку на Board и наследовать или комбинировать свои методы:
  • *.isValid() проверять только от-и-coords, они в настоящее время не проверяют, есть ли промежуточные дружественные или вражеские части, или действительно, если на месте занятыми. Я думаю, вы должны сделать это на одной функции на уровне Совета, это будет очень уродливо, если вы сделаете это для каждой части. Вам может понадобиться вспомогательная функция (итератор?) Для каждой части для генерации interveningSpots(fromX,fromY,toX,toY,pieceType), поэтому Board.validMove() может проверить, они заняты.

  • имя Piece.isValid() запутанно, это может означать либо validMove(), либо validPosition(). Лично я бы переименовал его validMove(). (Нам понадобится validPosition(), если вы когда-либо выполняете продвижение по службе, но опять же, это будет реализовано на уровне Совета, а не на Piece или Player).

  • King.isValid(), кажется, всегда возвращает false - ошибка?

  • Queen/Bishop/Rook.isValid() в настоящее время разрешают пустые нулевые перемещения (toX==fromX && toY==fromY). Это может звучать как nitpicking, но a) он может ошибочно позволить вам уклониться от помощника, «ничего не делая». B) он, вероятно, испортит рекурсию в любом AI, который вы или кто-то захотите добавить.

  • подсказка для более эффективного и компактного кода для King.isValid(): вам не нужно брать sqrt; просто проверьте, что dist2 = (dx ^ 2 + dy ^ 2) либо 1, либо 2. И вам не нужно abs (dx), так как квадрат отрицательного числа положителен. Итак:

    @Override
    public boolean isValid(...) {
        if(!super.isValid(...)) {
            return false;
    
        int dist2 = Math.pow((toX - fromX), 2) + Math.pow((toY - fromY), 2);
        return (dist2 == 1 || dist2 == 2);
    }
    
ответил smci 6 SatEurope/Moscow2014-12-06T05:22:44+03:00Europe/Moscow12bEurope/MoscowSat, 06 Dec 2014 05:22:44 +0300 2014, 05:22:44
0

Вот несколько способов упростить вашу логику:

  • Как уже упоминалось, вместо записи if (condition == true), напишите if (condition).
  • Аналогично, вместо if (condition == false), напишите if (!condition).
  • Если вы соблазняетесь написать:

    if (condition)
        return true;
    return false;
    

    ... вместо этого напишите условие return condition;.

  • Аналогично, вместо:

    if (condition)
        return false;
    return true;
    

    ... написать return (!condition);.

  • Если у вас возникает соблазн написать:

    if (condition1)
        return true;
    if (condition1)
        return true;
    return false;
    

    ... вместо return condition1 || condition2;

В общем, упростите логику, если она увеличится, уточните.

ответил ksnortum 19 Jpm1000000pmFri, 19 Jan 2018 18:56:04 +030018 2018, 18:56:04

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132