当前位置: 首页 > 知识库问答 >
问题:

跳棋与抽象棋的面向对象设计

澹台岳
2023-03-14

我列出了这两种游戏的相似之处,目的是可能将其扩展到国际象棋,但对我的设计提出质疑。

相似点

  • 件-物料(行、列)在板上的位置
  • 板-nxn片格
  • 游戏-包含棋盘和玩家列表
  • 播放机-播放机名称

差异

  • 在checkers中,您可以在不同的方向上移动,因此我创建了一个名为Checker的独立抽象类,它继承了Piece,并实现了一个返回pieces移动的Moveable接口。所以棋子和国王必须实施这个方法,提供它的招式。

问题和关切

  • 我之所以质疑这个设计,是因为我也可以创建一个抽象方法和一个接口,但这不能扩展到国际象棋。
  • 我也不知道如何将一个玩家与一个记号联系起来,比如在井字游戏中的x或o。我使用hashmap将播放机映射到其标记。
  • 我有一些复选框的代码,其中我有一个枚举颜色,但我也有一个变量标记,这是一样的东西。不确定如何解决此问题。
  • 可以对对象进行类型化吗?由于board不知道它有什么类型的棋子,所以我必须在跳棋课上这样做
List<List<Integer>> moves = ((Checker)piece).getPossibleMoves();

备注

我没有完成所有的实现,比如知道玩家什么时候赢了,或者处理边缘案件,或者当一个棋子变成了国王。为了简单起见,我还硬编码了球员的同时,只是想要反馈,如果这是一个好的方向。

java prettyprint-override">public class Piece {
    private String marker;
    protected int row;
    protected int col;

    public Piece(String marker, int row, int col) {
        this.marker = marker;
        this.col = col;
        this.row = row;
    }

    public String getMarker() {
        return marker;
    }

    public void setMarker(String marker) {
        this.marker = marker;
    }

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public int getCol() {
        return col;
    }

    public void setCol(int col) {
        this.col = col;
    }
}

public class Player {
    private String name;

    public Player(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Board {
    private Piece[][] grid;

    public Board(int rows, int cols) {
        grid = new Piece[rows][cols];
    }

    private boolean isSpotEmpty(int row, int col) {
        return grid[row][col] == null;
    }

    public void move(Piece piece) {
        if(isSpotEmpty(piece.getRow(), piece.getCol())) {
            grid[piece.getRow()][piece.getCol()] = piece;
        } else {
            throw new InvalidStateException("Invalid Move");
        }
    }

    public void showBoard() {
        for(int i = 0; i < grid.length; i++) {
            for(int j = 0; j < grid[0].length; j++) {
                Piece piece = grid[i][j];
                if(piece == null) {
                    System.out.print('*');
                } else {
                    System.out.print(piece.getMarker());
                }
            }
            System.out.println();
        }
    }

    public boolean isCellEmpty(int row, int col) {
        return grid[row][col] == null;
    }

    public String getMarker(int row, int col) {
        return grid[row][col].getMarker();
    }

    public Piece getPiece(int row, int col) {
        return grid[row][col];
    }

    public void removePiece(int row, int col) {
        grid[row][col] = null;
    }
}

public abstract class Game {
    protected List<Player> players;
    protected Board board;
    public abstract void startGame();
    public abstract Piece getPieceFromInput(Player player, String marker);
}




这是Tic Tac Toe


public class TicTacToe extends Game {
    private static final int BOARD_SIZE = 3;
    private HashMap<Player, String> playerMap;

    public TicTacToe() {
        board = new Board(BOARD_SIZE, BOARD_SIZE);
        players = new ArrayList<>();
        players.add(new Player("player1"));
        players.add(new Player("player2"));
        playerMap.put(players.get(0), "o");
        playerMap.put(players.get(1), "x");
    }

    @Override
    public void startGame() {
        boolean playerOneTurn = true;
        Player currPlayer;
        Piece piece;
        while(1 < 2) {

            currPlayer = (playerOneTurn) ? players.get(0) : players.get(1);
            piece = getPieceFromInput(currPlayer, playerMap.get(currPlayer));
            try {
                board.move(piece);
                playerOneTurn = !playerOneTurn;
            } catch(InvalidStateException e) {
                System.out.println(currPlayer.getName() + e.getMessage());
            }
            board.showBoard();
        }
    }

    @Override
    public Piece getPieceFromInput(Player player, String marker) {
        System.out.println(player.getName() + " Please enter move by row col");
        Scanner sc = new Scanner(System.in);
        int row = sc.nextInt();
        int col = sc.nextInt();
        return new Piece(marker, row, col);
     }


}


这是跳棋

public abstract class Checker extends Piece implements Movable {
    protected Color color;

    public Checker(String marker, int row, int col, Color color) {
        super(marker, row, col);
        this.color = color;
    }

}

public enum Color {
    RED, BLACK
}

public class King extends Checker {

    public King(String marker, int row, int col, Color color) {
        super(marker, row, col, color);
    }

    @Override
    public List<List<Integer>> getPossibleMoves() {
        List<List<Integer>> list = new ArrayList<>();
        //go up/down
        for(int i = 0; i < 8; i++) {
            if(i == row) continue;
            list.add(Arrays.asList(i, col));
        }

        //go horizontal
        for(int i = 0; i < 8; i++) {
            if(i == col) continue;
            list.add(Arrays.asList(row, i));
        }

        //go left diag
        for(int i = 0; i < 8; i++) {
            for(int j = col - row; j < 8; j++) {
                if(i == row && j == col) continue;
                list.add(Arrays.asList(i, j));
            }
        }
        return list;
    }
}

public class Pawn extends Checker {

    public Pawn(String marker, int row, int col, Color color) {
        super(marker, row, col, color);
    }

    @Override
    public List<List<Integer>> getPossibleMoves() {
        List<List<Integer>> list = new ArrayList<>();
        if(color == Color.RED) {
            list.add(Arrays.asList(row - 1,col - 1));
            list.add(Arrays.asList(row - 1,row + 1));
        } else {
            list.add(Arrays.asList(row + 1,row + 1));
            list.add(Arrays.asList(row + 1,row - 1));
        }
        return list;
    }
}

public interface Movable {
    List<List<Integer>> getPossibleMoves();
}

public class Checkers extends Game {
    private static final int BOARD_SIZE = 8;
    private Board board;
    private List<Player> players;
    private HashMap<Player, String> playerMap;

    public Checkers() {
        board = new Board(BOARD_SIZE, BOARD_SIZE);
        players = new ArrayList<>();
        players.add(new Player("alice"));
        players.add(new Player("bob"));
        playerMap = new HashMap<>();
        playerMap.put(players.get(0), "o");
        playerMap.put(players.get(1), "x");
    }

    @Override
    public void startGame() {
        setBoard();
        boolean playerOneTurn  = true;
        Player currPlayer = null;
        String playerMarker = "";
        while(1 < 2) {
            board.showBoard();
            currPlayer = (playerOneTurn) ? players.get(0) : players.get(1);
            playerMarker = playerMap.get(currPlayer);

            try {
                Piece selectedPiece = getPieceFromInput(currPlayer, playerMarker);
                setNewPiecePosition(currPlayer, selectedPiece);
                playerOneTurn = !playerOneTurn;

            } catch(InvalidStateException e) {
                System.out.println(e.getMessage());
            }
        }
    }

    private void setBoard() {
        for(int i = 0; i < 3; i++) {
            for(int j = 0; j < BOARD_SIZE; j++) {
               if((j + i) % 2 == 0) {
                   board.move(new Pawn("o", i, j, Color.BLACK));
               }
            }
        }

        for(int i = BOARD_SIZE - 1; i > BOARD_SIZE - 4; i--) {
            for(int j = 0; j < BOARD_SIZE; j++) {
                if((j + i) % 2 == 0) {
                    board.move(new Pawn("x", i, j, Color.RED));
                }
            }
        }
    }

    @Override
    public Piece getPieceFromInput(Player player, String marker) {
        System.out.println(player.getName() + " please select your piece from row : col");
        Scanner sc = new Scanner(System.in);
        int row = sc.nextInt();
        int col = sc.nextInt();
        if(board.isCellEmpty(row, col) ) {
            throw new InvalidStateException("You selected a wrong piece");
        } else if(!board.getMarker(row, col).equals(marker)) {
            throw new InvalidStateException("You selected the other players piece");

        }
        return board.getPiece(row, col);
    }

    private void setNewPiecePosition(Player player, Piece piece) {
        List<List<Integer>> moves = ((Checker)piece).getPossibleMoves();
        Scanner sc = new Scanner(System.in);
        System.out.println(player.getName() + " Please put your new piece to row : col");
        int row = sc.nextInt();
        int col = sc.nextInt();
        boolean isMoveValid = false;
        for(int i = 0; i < moves.size(); i++) {
            if(row == moves.get(i).get(0) && col == moves.get(i).get(1)){
                isMoveValid = true;
                break;
            }
        }
        if(!isMoveValid) {
            throw new InvalidStateException("Wrong move for selected piece");
        }
        board.removePiece(piece.getRow(), piece.getCol());
        piece.setRow(row);
        piece.setCol(col);
        board.move(piece);
    }
}

在这里玩游戏

public class GameMain {

    public static void main(String[] args) {
        Game checkers = new Checkers();
        Game tictactoe = new TicTacToe();
        checkers.startGame();
        tictactoe.startGame();
    }
}

共有1个答案

壤驷经国
2023-03-14

在https://gamedev.stackexchange.com/中发布这个问题可能是一个更好的论坛,但这里有一些我的观点,有些可能是错误的,但也许它们会有所帮助(看起来是一个有趣的项目)。

1)我之所以质疑这个设计,是因为我也可以创建一个抽象的方法和一个接口,但这不能扩展到国际象棋。

您可能希望继续使用接口,因为它们允许您以较少的重构来扩展功能,因为您可以实现多个接口,而不是扩展一个接口。如果需要,您还可以使用默认方法包括任何公共代码。

2)我也不知道如何将一个玩家与一个标记(例如在tic tac Toe中的x或o)联系起来。我使用一个hashmap来将一个玩家映射到它的标记。

在分配一个项目给作品的时候,你现在做的并没有什么问题。您可能需要考虑将其做得更好一些,就像piece implements Movable,drawable(返回到上面的接口注释)。关于棋子的存储位置,如果你想把棋子保存在游戏中,并且让游戏确定每一个棋子的所有可用移动和有效性。或者您可以将它们移到player.Collection中,如果玩家或棋子本身将负责确定棋盘上的可用性。

例如:

public TicTacToe() {
    this.players.add(new Player("Player 1", Color.RED, initPieces());
    this.players.add(new Player("Player 2", Color.BLACK, initPieces());
}

当玩家控制它的时候,红色会被应用在这个棋子上。例如,如果你有一个游戏,玩家可以偷取其他玩家的代币,向玩家添加新代币的行为会自动更新它的颜色。然后,在根据您正在执行的操作调用piece.draw()、piece.draw(canvas)时应用该颜色。

3)我有一些复选框的代码,我有一个枚举颜色,但我也有一个变量标记,这是一样的东西。不知道如何解决这个问题。

如果使用上面的注释,您可以执行以下操作:

Collection<Piece<String>> initPieces(String mark) {
    return Arrays.asList(new Piece<>(mark), ...);  // create 5
}

public Person(String name, Color color, Collection<Piece> initialPieces) {
    // for initialPieces set piece color to color
}

4)可以对一个对象进行类型化吗?因为board不知道它有什么类型的棋子,所以我必须在Checkers课上这样做

如果你在实现游戏时使用了可移动的界面,那么你就不需要施放了,这两个游戏/游戏板都知道:

List<List<Integer>> moves = piece.getPossibleMoves();

this.board = new Board<Checker>();

这将再次确保board.getPeice(x,y)将返回一个检查器,并且不需要强制转换。这取决于游戏后期可能会发生什么以及规则是什么。

5)基于棋子游戏实现棋子为王的边缘案例。

在大多数游戏中,结果是在移动发生后发生的。例如,正如您所提到的:

  • 如果跳过棋子,则将删除其他棋子
  • 跳棋如果到达棋盘的另一边,你将成为棋子的王
  • 如果棋手的棋子落在棋子上,您将移除棋子

在您的跳棋游戏示例中,您需要在处理成功移动的setNewPiecePosition之后调用一些东西。对于跳棋,您需要使用此方法:

  1. 检查开始和结束位置,并确定是否另一个玩家的棋子在中间。如果是,则从棋盘(和/或其他玩家的棋子列表)中删除该棋子。
  2. 确定玩家是否可以再次移动,以便在他们跳过某人时,同一玩家可以从更新的可用移动列表中再次选择。
  3. 确定新位置是否在棋盘的末尾,然后将棋子替换为玩家棋子集合中的国王。
 类似资料:
  • 本文向大家介绍c# 绘制中国象棋棋盘与棋子,包括了c# 绘制中国象棋棋盘与棋子的使用技巧和注意事项,需要的朋友参考一下 本文是利用C# 实现中国象棋的棋盘绘制,以及初始化布局,并不实现中国象棋的对弈逻辑。仅供学习参考使用。 思路: 绘制中国象棋棋盘,竖线九条,横线十条。再中间绘制‘楚河',‘汉界' 。 绘制棋子,然后将棋子布局在棋盘上即可。 涉及知识点: 用户控件:用于实现棋盘的绘制,重写 OnP

  • 我试图创建象棋游戏,所以我为象棋工具创建抽象类(皇后,国王,车...)我还创建了king工具来检查我的代码: 并创建game_board类: 问题是,当我尝试向矩阵添加对象时,它的show me错误:1 IntelliSense:不允许抽象类类型“King”的对象:纯虚函数“chess_tool::legal_movement”没有覆盖器 ....

  • 相友象棋是一个Andriod游戏,提供让两人用各自的设备在局网内,对下象棋的功能。 相友象棋是相友游戏框架的第一个实现。 相友象棋是一个eclipse工程,并且依赖Node4Android. 它没有android代码,src目录下为空,真正的代码在web目录下,将web目录下的文件打包成web.zip 然后放置于/res/raw目录下,和Node4Android一起生成,才能产生apk  

  • 我已经有一个Board对象,包含一个碎片列表。Piece是一个抽象类,有一个位置(x,y)和一个颜色(黑色或白色)。然后是King、Queen、Knight这三个类,实现了Piece类。 谢谢

  • “在面向对象编程中,抽象是对用户隐藏实现细节的过程,只有功能才会提供给用户。” 我一直在试图理解抽象,有人能告诉我们如何准确地隐藏实现细节吗?使用一个程序

  • 本文向大家介绍C++面向对象实现五子棋小游戏,包括了C++面向对象实现五子棋小游戏的使用技巧和注意事项,需要的朋友参考一下 尽量将面向对象的思想融入进程序中 ChessBoard.h ChessBoard.cpp Player.h Player.cpp main.cpp 以上所述就是本文的全部内容了,希望能够对大家熟练掌握C++有所帮助。