Triển khai Snakes and Ladders đơn giản trong Java

Sep 22 2020

Tôi đã tạo ra một triển khai rắn và thang khá thẳng về phía trước trong Java chỉ cho cái quái của nó. Chương trình có thể được tóm tắt là-

  1. Object of Board chứa một mảng Type Tile để đại diện cho bảng thực tế.

  2. Đối tượng kiểu Tile chứa một thành viên dữ liệu Enum để xác định xem nó là một ô thông thường hay một con rắn / bậc thang.

    Cũng chứa một ô đích để xác định điểm đến con rắn / bậc thang.

  3. Đối tượng người chơi chứa tên người chơi và vị trí người chơi.

  4. Khi khởi tạo, thông qua các ngẫu nhiên, tôi tạo ra các ô ngẫu nhiên để làm rắn và thang. Sau đó, người dùng được yêu cầu nhập không. của người chơi, và sau đó tiếp tục tung xúc xắc cho đến khi người chơi đạt đến ô thứ 100.

Mật mã:

Lớp BoardImpl:

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class BoardImpl implements Board{

    Tile[] board = new Tile[101];         //A array to represent the 'board'
    List<Player> players = new ArrayList<>();
    SecureRandom secureRandom = new SecureRandom();
    Set<Integer> occupiedPositions = new HashSet<>();

    public void setPlayers(List<Player> players) {
        this.players = players;
    }

    public Tile[] getBoard() {
        return board;
    }

    public List<Player> getPlayers() {
        return players;
    }

    public void addPlayers(Player player) {
        this.players.add(player);
    }

    @Override
    public void initialize() {
//this function generates the board, placing random snakes and ladders and
//filling the rest with normal tiles
        System.out.println("Initializing board");

        int snakes = secureRandom.nextInt(15) + 1; //generates snakes & 
        int ladders = secureRandom.nextInt(15) + 1; //ladders at random
                                                    //positions
        setSnakes(snakes);
        setLadders(ladders);

        for(int i = 0; i < 101; i++){
            if(board[i] != null){
                continue;
            }
            Tile t  = new TileImpl(TileType.NORMAL);
            board[i] = t;
        }


    }

    @Override
    public void movePlayer(Player player, int places) {
        int currentPosition = player.getPosition();
        String checkPos = checkEndingPosition(currentPosition, places);
        if(checkPos.equals("winner")){
                winner(player);
        }
        if(checkPos.equals("outOfBoard")){
            System.out.println("That move cannot be made from your position! " +
                    "Roll a different number next time");
        }
        if(checkPos.equals("valid")){
                Tile t = getTile(currentPosition + places);
                if(t.getType().equals(TileType.NORMAL)){
                    player.setPosition(currentPosition + places);
                    System.out.println("Player " + player.getName() +
                            " moves to position "+ player.getPosition());
                }else {
                    player.setPosition(t.getDestination());
                    System.out.println(" Player " + player.getName() +
                            " encountered a "+ t.getType()+ "!!, moves to position "+ player.getPosition());
                }
        }
    }

    @Override
    public Tile getTile(int n) {
        return board[n];
    }

    public String checkEndingPosition(int currentPosition, int places){

        if((currentPosition + places) > 100){
            return "outOfBoard";
        }else if((currentPosition+ places) == 100){
            return "winner";
        }
        else{
            return "valid";
        }
    }

    public void winner(Player player){

        System.out.print("Player " + player.getName() + " has won the game !");
        System.exit(0);
    }

    public void setSnakes(int n){
//this function generates snakes at random positions
        for(int i = 0; i < n; i ++){
            boolean flag = true;
            int start = 0;
            int dest = 0;

            while(flag) {
                 start = secureRandom.nextInt(98);
                if(!occupiedPositions.contains(start)){
                    occupiedPositions.add(start);
                    break;
                }
            }

            while(flag) {
 //Setting the destination for a snake tile to lower than its position
                 dest = (int)(start * secureRandom.nextDouble());
                if(!occupiedPositions.contains(dest)){
                    occupiedPositions.add(dest);
                    break;
                }
            }

            Tile tile = new TileImpl(TileType.SNAKE, dest);
            board[start] = tile;

            System.out.println("Created snake " + "[" + start+ "," + dest + "]");
        }

    }

    public void setLadders(int n) {
//this function generates ladders randomly
        for (int i = 0; i < n; i++) {
            boolean flag = true;
            int start = 0;
            int dest = 0;

            while (flag) {
                start = secureRandom.nextInt(80);
                if (!occupiedPositions.contains(start)) {
                    occupiedPositions.add(start);
                    break;
                }
            }

            while (flag) {
//this step places the destination for the ladder tile to greater than its position
                dest = (int) (start + secureRandom.nextDouble() * 10);
                if (!occupiedPositions.contains(dest)) {
                    occupiedPositions.add(dest);
                    break;
                }
            }

            Tile tile = new TileImpl(TileType.LADDER, dest);
            board[start] = tile;
            System.out.println("Created ladder " + "[" + start+ "," + dest + "]");
        }
    }
}

Lớp PlayerImpl:

public class PlayerImpl implements Player{


    private int position = 0;
    private String name = null;

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

    @Override
    public int getPosition() {
        return position;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setPosition(int currentPosition) {
        this.position = currentPosition;
    }

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

Lớp TileImpl:

public class TileImpl implements Tile {

    private TileType type;
    private int position;
    private int destination;

    public TileImpl(TileType type) {
        this.type = type;
    }

    public TileImpl(TileType type, int dest) {
        this.type = type;
        this.destination = dest;
    }

    public int getPosition() {
        return position;
    }

    public int getDestination() {
        return destination;
    }

    public void setType(TileType type) {
        this.type = type;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    public void setDestination(int destination) {
        this.destination = destination;
    }

    @Override
    public TileType getType() {
        return type;
    }

}

Lớp SnakesAndLadders:

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class SnakesAndLadders {

    public static void main(String args[]){

        Board board = new BoardImpl();
        List<Player> players = new ArrayList<>();
        System.out.println("How many players ?");

        Scanner scan = new Scanner(System.in);
        int noOfPlayers = scan.nextInt();

        for(int i = 0; i < noOfPlayers; i++){
            System.out.println("Enter name for player "+ i);
            String name = scan.next();
            Player player = new PlayerImpl(name);
            players.add(player);

        }

        board.initialize();
        int counter = 0;
        System.out.println("Lets Play!");
        String choice = "";
        SecureRandom random = new SecureRandom();
//simulating a game through the do while loop
        do{
            if(counter >= noOfPlayers) counter = 0;
            Player currPlayer = players.get(counter);
            System.out.println(" Player " + currPlayer.getName() + " turn to play!");
            System.out.println(" R = Roll the dice, Q = quit");
            choice = scan.next();
            if(choice.equalsIgnoreCase("R")){
                int places = random.nextInt(6) + 1;
                board.movePlayer(currPlayer, places);
                counter++;

            }
        }while(!choice.equalsIgnoreCase("Q"));
    }
}

Tôi nghĩ trận đấu rất thẳng về phía trước. Tôi muốn đặc biệt biết nếu

  1. Có một cách tốt hơn để tạo ra số lượng rắn và thang ngẫu nhiên.

  2. Cấu trúc dữ liệu tốt hơn và thích hợp hơn để đại diện cho 'hội đồng quản trị'

  3. Nếu mã của tôi có thể được cải thiện để chứa bất kỳ mô hình / thực hành quan trọng nào.

Trả lời

2 Marc Sep 23 2020 at 09:48

Chào mừng bạn đến với Đánh giá mã. Triển khai tốt và cấu trúc tốt của mã, tìm các đề xuất của tôi theo dòng.

  1. Có một cách tốt hơn để tạo ra số lượng rắn và thang ngẫu nhiên.

Các phương pháp setSnakesvà chỉ setLadderskhác nhau một vài dòng mã, đó là một dấu hiệu của sự trùng lặp mã. Đây là phương pháp setLadders:

public void setLadders(int n) {
//this function generates ladders randomly
    for (int i = 0; i < n; i++) {
        boolean flag = true;
        int start = 0;
        int dest = 0;

        while (flag) {
            start = secureRandom.nextInt(80);
            if (!occupiedPositions.contains(start)) {
                occupiedPositions.add(start);
                break;
            }
        }

        while (flag) {
//this step places the destination for the ladder tile to greater than its position
            dest = (int) (start + secureRandom.nextDouble() * 10);
            if (!occupiedPositions.contains(dest)) {
                occupiedPositions.add(dest);
                break;
            }
        }

        Tile tile = new TileImpl(TileType.LADDER, dest);
        board[start] = tile;
        System.out.println("Created ladder " + "[" + start+ "," + dest + "]");
    }
}

Một cách tiếp cận là tạo ra một phương pháp để tạo ra các số ngẫu nhiên và sau đó sử dụng nó để tạo thang và rắn. Ví dụ:

private void setRandomLadder() {
    while (true) {
        int[] segment = getRandomSegment(1,99);
        int start = segment[0];
        int end = segment[1];
        if (isTileFree(start) && isTileFree(end)) {
            board[start] = new TileImpl(TileType.LADDER, end);
            board[end] = new TileImpl(TileType.OCCUPIED);
            break;
        }
    }
}

Phương pháp này có thể được gọi nlần tùy thuộc vào số bậc thang bạn muốn thêm vào bảng. Phương thức getRandomSegmenttrả về hai điểm ngẫu nhiên từ 1 đến 99. Ngoài ra, tôi đã thay đổi tên từ setLaddersthành setRandomLadder, để làm cho nó tự mô tả. Cuối cùng, hãy nhớ rằng nếu bạn thêm quá nhiều thang, các phương pháp như vậy sẽ không bao giờ hoàn thành, vì vậy hãy xem xét để kiểm tra xem có ô trống trước đó không.


  1. Cấu trúc dữ liệu tốt hơn và thích hợp hơn để đại diện cho 'hội đồng quản trị'

Bằng cách sử dụng phương pháp trước, chúng tôi có thể xóa danh sách occupiedPositions, điều này có thể thực hiện được bằng cách thêm TileType.OCCUPIED.

Mảng của Tilelà cấu trúc dữ liệu ok, một giải pháp thay thế là sử dụng ArrayList.


  1. Nếu mã của tôi có thể được cải thiện để chứa bất kỳ mô hình / thực hành quan trọng nào.
  • Đầu ra bảng điều khiển :

    How many players ?
    2
    Enter name for player 0
    

    Thật khó hiểu cho người dùng khi thấy trình phát 0, bắt đầu từ trình phát 1.

  • Magic số : new Tile[101], secureRandom.nextInt(98), vv Tạo hằng, ví dụ:

    private static final int SIZE = 101;
    
  • Modifier : các phương thức setSnakesvà setLaddersphải là private, giống nhau cho tất cả các biến phiên bản.

  • Khởi tạo trong hàm tạo : BoardImplcó một phương thức initialize()nhưng và hàm tạo rỗng. Bạn có thể sử dụng trực tiếp hàm tạo.

  • Encapsulation : giao diện Boardchấp nhận a Playervới public setPosition, có nghĩa là bất kỳ ai cũng có thể thay đổi nó. Chỉ có hội đồng quản trị nên thay đổi trạng thái của người chơi. Một giao diện an toàn hơn có thể là:

    public interface Board {
      void movePlayer(String playerId, int places);
      //..
    }
    
  • Tránh các biến tạm thời : thay vì

    Tile tile = new TileImpl(TileType.LADDER, dest);
    board[start] = tile;
    

    Bạn có thể viết:

    board[start] = new TileImpl(TileType.LADDER, dest);
    
  • Mùi mã :

        public String checkEndingPosition(int currentPosition, int places){
    
          if((currentPosition + places) > 100){
              return "outOfBoard";
          }else if((currentPosition+ places) == 100){
              return "winner";
          }
          else{
              return "valid";
          }
      }
    

    Trả về một chuỗi rất dễ xảy ra lỗi ở đây. Ví dụ, bạn có thể trả về một boolean và cuối cùng là một ngoại lệ.

  • Kiểm tra và khả năng sử dụng lại : phương thức winnergọi System.exit(0), khiến nó không thể kiểm tra. Ngoài ra, có rất nhiều System.out.printlntrong BoardImpllớp. Rất khó để kiểm tra một lớp học như vậy. Tốt nhất, lớp Boardkhông bao giờ được in cho người dùng, chỉ để gỡ lỗi. Các mainphải có trách nhiệm để tương tác với người dùng.

3 user985366 Sep 22 2020 at 15:53

Khả năng đọc và phong cách

Vui lòng chạy mã của bạn thông qua trình định dạng để làm cho mã dễ đọc hơn và tuân theo các tiêu chuẩn.

Cái này có thể hoạt động:

https://www.tutorialspoint.com/online_java_formatter.htm

Cờ?

            boolean flag = true;
            int start = 0;
            int dest = 0;

            while (flag) {
                start = secureRandom.nextInt(80);
                if (!occupiedPositions.contains(start)) {
                    occupiedPositions.add(start);
                    break;
                }
            }

Bạn đang không sử dụng, hãy kiểm tra hoặc thay đổi cờ ở đây. Vòng lặp này chỉ nên như vậy while(true)và vòng lặp tiếp theo cũng làm điều tương tự.

Nếu khác-

if(checkPos.equals("winner")){
                winner(player);
        }

Khi bạn đang kiểm tra một số trường hợp độc quyền và so sánh với một chuỗi, tôi nghĩ rằng một switchcâu lệnh là cách thích hợp / tiêu chuẩn hơn để làm điều đó. Nó cũng làm rõ ràng hơn rằng các trường hợp là độc quyền.

Cấu trúc dữ liệu

Set<Integer> occupiedPositions = new HashSet<>();

Điều này có vẻ không cần thiết / không tự nhiên đối với tôi. Tôi sẽ thêm vào lớp Tile a boolđể đánh dấu một ô là bị chiếm dụng, và sau đó thông tin đó có thể được chứa trong chính ô / bảng thay vì có một ô riêng biệt Setđể ghi sổ.