Eine einfache Implementierung von Snakes and Ladders in Java
Ich habe eine ziemlich einfache Implementierung von Schlangen und Leitern in Java erstellt, nur zum Teufel. Das Programm kann wie folgt zusammengefasst werden:
Das Board-Objekt enthält ein Array von Type Tile, um das tatsächliche Board darzustellen.
Objekt vom Typ Kachel enthält ein Enum-Datenelement, um zu identifizieren, ob es sich um eine normale Kachel oder eine Schlange / Leiter handelt.
Enthält auch eine Zielkachel zur Identifizierung des Schlangen- / Leiterziels.
Das Spielerobjekt enthält den Spielernamen und die Spielerposition.
Bei der Initialisierung generiere ich durch Zufälle zufällige Kacheln als Schlangen und Leitern. Dann wird der Benutzer aufgefordert, no einzugeben. von Spielern, und würfeln Sie dann weiter, bis ein Spieler das 100. Plättchen erreicht.
Der Code:
Klasse 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 + "]");
}
}
}
Klasse 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;
}
}
Klasse 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;
}
}
Klasse 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"));
}
}
Ich denke, das Spiel ist sehr einfach. Ich wollte besonders wissen, ob
Es gibt eine bessere Möglichkeit, die Zufallszahlen von Schlangen und Leitern zu generieren.
Eine bessere und angemessenere Datenstruktur zur Darstellung des "Boards"
Wenn mein Code verbessert werden kann, um wichtige Paradigmen / Praktiken zu enthalten.
Antworten
Willkommen bei Code Review. Gute Implementierung und gute Struktur des Codes, finde meine Vorschläge in der Linie.
- Es gibt eine bessere Möglichkeit, die Zufallszahlen von Schlangen und Leitern zu generieren.
Die Methoden setSnakesund setLaddersunterscheiden sich nur durch ein paar Codezeilen, was ein Indikator für die Codeduplizierung ist. Dies ist die Methode 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 + "]");
}
}
Ein Ansatz besteht darin, eine Methode zum Generieren der Zufallszahlen zu erstellen und diese dann zum Generieren von Leitern und Schlangen zu verwenden. Zum Beispiel:
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;
}
}
}
Diese Methode kann nje nach Anzahl der Leitern, die Sie dem Board hinzufügen möchten, mal aufgerufen werden . Die Methode getRandomSegmentgibt zwei zufällige Punkte zwischen 1 und 99 zurück. Außerdem habe ich den Namen von setLaddersin geändert setRandomLadder, um ihn selbstbeschreibend zu machen. Denken Sie schließlich daran, dass solche Methoden, wenn Sie zu viele Leitern hinzufügen, niemals abgeschlossen werden. Überprüfen Sie daher, ob zuvor freie Kacheln vorhanden sind.
- Eine bessere und angemessenere Datenstruktur zur Darstellung des "Boards"
Mit der vorherigen Methode können wir die Liste entfernen occupiedPositions, was durch Hinzufügen möglich war TileType.OCCUPIED.
Das Array von Tileist als Datenstruktur in Ordnung, eine Alternative ist zu verwenden ArrayList.
- Wenn mein Code verbessert werden kann, um wichtige Paradigmen / Praktiken zu enthalten.
Konsolenausgabe :
How many players ? 2 Enter name for player 0Es ist verwirrend für den Benutzer, Spieler 0 zu sehen und mit Spieler 1 zu beginnen.
Magische Zahlen :
new Tile[101],secureRandom.nextInt(98)etc. erstellen Konstanten, zum Beispiel:private static final int SIZE = 101;Modifikatoren : Die Methoden
setSnakesundsetLadderssollten privat sein, für alle Instanzvariablen gleich.Im Konstruktor initialisieren : Der
BoardImplhat eine Methode,initialize()aber einen leeren Konstruktor. Sie können den Konstruktor direkt verwenden.Kapselung : Die Schnittstelle
Boardakzeptiert einePlayermit einer ÖffentlichkeitsetPosition, was bedeutet, dass jeder sie ändern kann. Nur das Brett sollte den Status des Spielers ändern. Eine sicherere Schnittstelle könnte sein:public interface Board { void movePlayer(String playerId, int places); //.. }Vermeiden Sie temporäre Variablen : statt
Tile tile = new TileImpl(TileType.LADDER, dest); board[start] = tile;Du kannst schreiben:
board[start] = new TileImpl(TileType.LADDER, dest);Code Geruch :
public String checkEndingPosition(int currentPosition, int places){ if((currentPosition + places) > 100){ return "outOfBoard"; }else if((currentPosition+ places) == 100){ return "winner"; } else{ return "valid"; } }Das Zurückgeben eines Strings ist hier sehr fehleranfällig. Beispielsweise könnten Sie einen Booleschen Wert und schließlich eine Ausnahme zurückgeben.
Testen und Wiederverwendbarkeit : Die Methode
winnerruft aufSystem.exit(0), was das Testen unmöglich macht. Darüber hinaus gibt es vieleSystem.out.printlnin derBoardImplKlasse. Es ist sehr schwer, eine solche Klasse zu testen. Im Idealfall sollte die KlasseBoardniemals zum Debuggen auf dem Benutzer gedruckt werden. Dermainsollte für die Interaktion mit dem Benutzer verantwortlich sein.
Lesbarkeit und Stil
Bitte führen Sie Ihren Code durch einen Formatierer, um ihn besser lesbar zu machen und den Standards zu folgen.
Dieser kann funktionieren:
https://www.tutorialspoint.com/online_java_formatter.htm
Flagge?
boolean flag = true;
int start = 0;
int dest = 0;
while (flag) {
start = secureRandom.nextInt(80);
if (!occupiedPositions.contains(start)) {
occupiedPositions.add(start);
break;
}
}
Sie verwenden, überprüfen oder ändern hier nicht die Flagge. Diese Schleife sollte einfach sein while(true)und die nächste Schleife macht dasselbe.
Ansonsten-
if(checkPos.equals("winner")){
winner(player);
}
Wenn Sie mehrere exklusive Fälle prüfen und mit einer Zeichenfolge vergleichen, ist eine switchAnweisung meiner Meinung nach eine geeignetere / standardmäßigere Methode. Es macht auch klarer, dass die Fälle exklusiv sind.
Datenstruktur
Set<Integer> occupiedPositions = new HashSet<>();
Das erscheint mir unnötig / unnatürlich. Ich würde der Kachelklasse a hinzufügen, boolum eine Kachel als belegt zu markieren, und dann können diese Informationen in der Kachel / Tafel selbst enthalten sein, anstatt eine separate Setfür die Buchhaltung zu haben.