Lab week 8 – Implementing the Command Pattern and NPCs Codebase This lab continues from last week. You need to have completed lab 7 to continue with this lab sheet and you are expected to use your own...

1 answer below »
Need as soon as


Lab week 8 – Implementing the Command Pattern and NPCs Codebase This lab continues from last week. You need to have completed lab 7 to continue with this lab sheet and you are expected to use your own code from last week to work on this week’s tasks. Implementing the Command Pattern The DungeonMaster class is already looking very messy (and is only going to get worse as we continue to add commands to the game). If we consider the current sequence diagram for processing a turn: We can see that already I have had to simplify the processCommand logic (actually what is really going on is more complicated), and we have already started to break things up with dedicated methods within DungeonMaster to help deal with the logic (like processMove). This is only going to get worse over time. One way we can clean this up is to introduce dedicated classes to handle each command. Luckily for us, this problem has been tackled before and there is a dedicated command pattern which comes to our rescue! Let’s start by specifying an abstract class to represent commands in general. Create a new interface called Command in the Control package as follows: package mazegame.control; import mazegame.entity.Player; public interface Command { public abstract CommandResponse execute(ParsedInput userInput, Player thePlayer); } We also need a CommandResponse class to provide a response back to the player after each command has been processed. package mazegame.control; public class CommandResponse { private Boolean finishedGame; private String message; public CommandResponse(String message) { this.message = message; finishedGame = false; } public CommandResponse(String message, Boolean quitFlag) { this.message = message; finishedGame = quitFlag; } public void setFinishedGame(Boolean quitFlag) { finishedGame = quitFlag; } public boolean isFinishedGame() { return finishedGame; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } We are now in a position to write a command class for move and another for quit. package mazegame.control; import mazegame.entity.Player; public class QuitCommand implements Command { public CommandResponse execute (ParsedInput input, Player thePlayer) { return new CommandResponse ("Thanks for playing --- Goodbye", true); } } That’s it for Quit (about the easiest command you could imagine!). Now let’s create a class for Move: package mazegame.control; import mazegame.entity.Exit; import mazegame.entity.Player; public class MoveCommand implements Command { public CommandResponse execute (ParsedInput userInput, Player thePlayer) { String exitLabel = (String) userInput.getArguments().get(0); Exit desiredExit = thePlayer.getCurrentLocation().getExit(exitLabel); if (desiredExit == null) { return new CommandResponse("There is no exit there . . . try moving somewhere else!"); } thePlayer.setCurrentLocation(desiredExit.getDestination()); return new CommandResponse("You find yourself looking at . . ." + thePlayer.getCurrentLocation().getDescription()); } } So now we have defined our command classes, but we haven’t implemented them yet. Before we change DungeonMaster, let’s introduce one more class to keep track of the available commands. Create a new Control class called CommandHandler as follows: package mazegame.control; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import mazegame.entity.Player; public class CommandHandler { private HashMap availableCommands; private ArrayList commands; private Parser theParser; public CommandHandler () { availableCommands = new HashMap(); commands = new ArrayList(); setupCommands(); theParser = new Parser(popArrayList()); } private void setupCommands () { availableCommands.put("go", new MoveCommand()); availableCommands.put("quit", new QuitCommand()); availableCommands.put("move", new MoveCommand()); } private ArrayList popArrayList () { Set set = availableCommands.keySet(); ArrayList temp = new ArrayList (); for (String key : set) { temp.add(key); } return temp; } public CommandResponse processTurn (String userInput, Player thePlayer) { ParsedInput validInput = theParser.parse(userInput); Command theCommand = (Command) availableCommands.get(validInput.getCommand()); return theCommand.execute(validInput, thePlayer); } } So here we maintain a Hashtable for each command class, indexed by a label. The beauty of this is: 1. We can set up aliases for different commands (such as move and go pointing to the same command). 2. When we develop new command classes we simply define the class and add it to the hashtable with a label and it works. OK now we have our CommandHandler, it is time to change DungeonMaster: package mazegame.control; import mazegame.boundary.IMazeClient; import mazegame.boundary.IMazeData; import mazegame.entity.Player; public class DungeonMaster { private IMazeClient gameClient; private IMazeData gameData; private Player thePlayer; private CommandHandler playerTurnHandler; public DungeonMaster(IMazeData gameData, IMazeClient gameClient) { this.gameData = gameData; this.gameClient = gameClient; playerTurnHandler = new CommandHandler(); } public void printWelcome() { gameClient.playerMessage(gameData.getWelcomeMessage()); } public void setupPlayer() { String playerName = gameClient.getReply("What name do you choose to be known by?"); thePlayer = new Player(playerName); thePlayer.setCurrentLocation(gameData.getStartingLocation()); gameClient.playerMessage("Welcome " + playerName + "\n\n"); gameClient.playerMessage("You find yourself looking at "); gameClient.playerMessage(gameData.getStartingLocation().getDescription()); // gameClient.getReply(">"); } public void runGame() { printWelcome(); setupPlayer(); while (handlePlayerTurn()) { // handle npc logic later } gameClient.getReply("\n\n>"); } private boolean handlePlayerTurn() { CommandResponse playerResponse = playerTurnHandler.processTurn(gameClient.getCommand(), thePlayer); gameClient.playerMessage(playerResponse.getMessage()); return !playerResponse.isFinishedGame(); } } Our code is now cleaner and we have a flexible, extensible structure to continue adding commands. The revised process player turn sequence diagram looks like: A problem occurs when we have invoke the move command with no arguments and our arguments ArrayList in ParsedInput is empty. We can fix this easily with a bit of defensive coding in MoveCommand as follows: package mazegame.control; import mazegame.entity.Exit; import mazegame.entity.Player; public class MoveCommand implements Command { public CommandResponse execute (ParsedInput userInput, Player thePlayer) { String exitLabel = (String) userInput.getArguments().get(0); Exit desiredExit = thePlayer.getCurrentLocation().getExit(exitLabel); if (desiredExit == null) { return new CommandResponse("There is no exit there . . . try moving somewhere else!"); } thePlayer.setCurrentLocation(desiredExit.getDestination()); return new CommandResponse("You find yourself looking at . . ." + thePlayer.getCurrentLocation().getDescription()); } } Now run your game for a little gameplay test, to verify it is behaving how you would like: I’m still not quite happy with the information I get from the game when I move from one location to another. So I am going to change how the results are presented. Let’s start by developing a ToString method for Location. package mazegame.entity; import java.util.HashMap; public class Location { private HashMap exits; private String description; private String label; public Location () { } public Location (String description, String label){ this.setDescription(description); this.setLabel(label); exits = new HashMap(); } public boolean addExit (String exitLabel, Exit theExit){ if (exits.containsKey(exitLabel)) return false; exits.put(exitLabel, theExit); return true; } public Exit getExit(String exitLabel){ return (Exit) exits.get(exitLabel); } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String availableExits() { StringBuilder returnMsg = new StringBuilder(); for (Object label: this.exits.keySet()) { returnMsg.append("[" + label.toString() + "] "); } return returnMsg.toString(); } public String toString() { return "**********\n" + this.label + "\n**********\n" + "Exits found :: " + availableExits() + "\n**********\n" + this.description + "\n**********\n"; } } Now I can change my MoveCommand class accordingly: package mazegame.control; import mazegame.entity.Exit; import mazegame.entity.Player; public class MoveCommand implements Command { public CommandResponse execute (ParsedInput userInput, Player thePlayer) { if (userInput.getArguments().size() == 0) { return new CommandResponse ("If you want to move you need to tell me where."); } String exitLabel = (String) userInput.getArguments().get(0); Exit desiredExit = thePlayer.getCurrentLocation().getExit(exitLabel); if (desiredExit == null) { return new CommandResponse("There is no exit there . . . try moving somewhere else!"); } thePlayer.setCurrentLocation(desiredExit
Answered Same DayOct 09, 2021ITECH7201

Answer To: Lab week 8 – Implementing the Command Pattern and NPCs Codebase This lab continues from last week....

Abr Writing answered on Oct 14 2021
125 Votes
Lavf57.66.105
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here