Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

So, We need to turn in two classes for this one. A BotController and BotStatemen

ID: 3873152 • Letter: S

Question

So, We need to turn in two classes for this one. A BotController and BotStatement. BotController will take input that the user supplies, validate it, and then give the model object what it needs to do its work. What the model needs is a description of the robot's world and a list of the program statements that the robot should follow.

BotController

Your controller will have the ability to read a new description of the robot's world, the ability to read in a new program, and the ability to start a simulation. Write a class called BotController with the following public methods (it should have no other public methods except a constructor if you want it).

public void readWorld(Scanner in)

readWorld should read lines from the Scanner until there are no more lines to read. Each line will represent a row in the robot's world. The line is allowed to have whitespace characters (spaces and tabs) and the four characters (r, w, b, t). The total number of (r, w, b, t) characters found should be the same on each line, and this number indicates the number of columns in the robot's world. The input lines should have at least one row and at least one column. There should be exactly one r. If any of these specifications are violated or in is null an IllegalArgumentException should be thrown.

- In this method, we read a file which has the chars for making a 2D array and store each line into a String variable. Process that string and make a 2D array from it, for as long as it has the character r,w,b,t. Basically, we need to do an error checking afterwards.

File would look like this: wwwt We also need to assume that we don't know how many lines there would be.

wbwr

public void readProgram(Scanner in)

readProgram should read lines from the Scanner until there are no more lines to read. For each line, you should first remove any comment and then any whitespace from the beginning and end of the de-commented string. (A comment begins with the first '#' and goes to the end of the line.) If the resulting string is empty the line can be ignored. Otherwise, the modified line should be used to construct a new BotStatement object. If the modified line is an illegal program statement, the construction of the BotStatement will fail with an IllegalArgumentException. Your readProgram method should not catch this exception and instead should allow it to continue to the caller of readProgram. You should document that an IllegalArgumentException is thrown if a program is illegally formatted.

Note that readWorld and readProgram can each be called more than once, but it is always the most recent call to either of these methods that defines what the robot's world and program.

public void start()

When start is called, a BotModel object should be created and the most recently read world and program should be passed to its constructor. If not both a world and program have been read, an IllegalStateException should be thrown.

The world should be represented as a two-dimensional array of char, following the same conventions used in Project 1 to represent the robot's world. The program should be represented as an array of BotStatement with the first element of the array being the first statement of the program and the last element of the array being the last statement of the program.

BotStatement

Each BotStatement represents a single Bot program statement (eg, 0 x*** -> n 0). Write a class called BotStatement with the following public methods (it should have no other public methods).

public BotStatement(String s)

If you consider each grouping of non-whitespace characters in the string to be "tokens", then the sequence of tokens in the string should be: an integer (the trigger state), a four-character string (the trigger sensors), the string "->" (unused), a single character (the action), and an integer (the new state). The four-character token should have the first character one of n/x/*, the second one of e/x/*, the third one of w/x/*, and the fourth one of s/x/*. The single action character should be one of n/e/w/s/x. Any amount of whitespace is allowed before, between and after each token, but these should be the only tokens in the string and they should occur in this order. If any of these expectations are broken, an IllegalArgumentException should be thrown.

public boolean match(int state, boolean n, boolean e, boolean w, boolean s)

This method will be used to match a robot's current state and sensor readings against a program statement to see if they are a match. The parameters n/e/w/s will each be true if there is a wall in the immediately adjacent square. If state does not match "the trigger state" return false. If n is true and the first character of the trigger sensors is x, or n is false and the first character of the trigger sensors is n, return false. The remaining parameters should behave similarly to n except with the second, third and fourth characters of the trigger sensors. If none of these tests causes the method to return false, then true should be returned.

public int nextState()

This method should return the new state value found by the constructor.

public char nextAction()

This method should return the action value found by the constructor.

You will write a complete BotModel class in a later project, but you will need a BotModel class in this project to allow your BotController to create a BotModel object. Use the following class to allow compilation (but don't turn it in).

public class BotModel {

public BotModel(char[][] world, BotStatement[] program) {

}

}

Any hints or help is much appreciated. Thank you.

Overview of Project 2 You are going to write and in two days for Project 2: SimpleautController and SimplotSlusionment simpleBotController will take input that the user uppiesvalidate it, and then give the model othieve what it needs to do its work. What the model needs is a desciption of the robots rld and sist of the program Simple BotController tiny to and a A sciplin of the roofs Is the ability to read in a new rogram, and the ability to start a simulation Y to a Rss called 5 impenettontrol ing outclie methovita it should have no other public methods empt a consinister Yyou want roadWorld should read lines from the scanner unt there are no more ines to read. Each line all represent a row in the robof L world. The line is allowed to have whitespace characters (Epaces and tabs) and the four characters r, a by . The total number at (, b. t} characters to und should be the same on Schlins and this number indicates the number of columns in the robots world. The input lines should have at least one row and at least one column. There should be exactly one. If any of these specifications are violated ar in is not an illegalRegentExeteoron should be thrown. sePuyam skil read in from the same until their HIH up more lives to read. Fut each line you should first return a corrent and tha Ina can hA ignored otherwise Sirplatest ament object it the modified Ina is Instead should allow it to continue to the caller of reacProgram. You should document that an IlegalurgumentException is thrown if a program is illegally formatted . Heyiriny Hined and of the Japarmeulueling A the first and goes to this end of the line in the IHsultiny the similar state IlegalArgument aption. We Nule that restwuild and randProgenical and be called more than once, bu, it is always the must recent call writer of these meth that our In the lotte ind pay Whan start is called a simpletotindal object should be created and the mast racAntly read world and pr should be passed to its caratnuctor reat hath a world and program have baan read, an 11egates to taxpaper on should be than in lost The world should be repre: timely bring the best talent of the pyra Project 1 to repre orld. The progra uld be array of SimpleBotStatement with the first sement of the cing the first stateme of the progra and the last element of SimpleBotStatement Each simpleBoissionment spent inyle Single B. Bog Hu slain 1 Win ile class called ArpieMotta with the full eli: This is soul live no other pubic metres impletot string If you consider Aarch groupin at nonwhitepanese characters in the string to ha Tokars" en thA sagAn at tak thA string a ould be an integritha triggar start ), a four-character sting the tripar Ansors, the string" united a single character that action, and an integrithan statR) The four-character talks should have the first character one af no, the accord and ot one, the third one of war and the fourth one or so. The single action character should be one of ninex. Any amount of whitespace is allowed before, betwoon and atter each taken, but these should be the anty tokens in the string and they hauls if any of these expectations are broken an ItalixpensetExceptide should be thrown THis mail will be used to match a robots Bur fings against a program statement see if they Hennials will sh be true if there is a well in Initiately adjacent square. In "The vigor allrun Solis II is true and the final character at the trigger sensors ex be false and that character at the trigger sensors is in return else. The remaining paramatars should behave similarly to a except with the sacond, third and outh characters of the triggar sensors. H none at these tacts causes the method to rahum talks, then true should be rehimed This method should return the new state value found by the constructor. public shy extRaton This met should return the action value found by the cut uctur SimpleBotModel a complete SimpleBoisodel d in later project, but you will need a SimpleDuModel was in this project, two alue your SimovieBuController to create a SimpleEMA I object U; the following class to allow compilation but dont turn tip. plotted guilt9 LElcotia de Ice Eld, geople

Explanation / Answer

package me.ramswaroop.jbot.core.slack;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;

import me.ramswaroop.jbot.core.slack.models.Event;
import me.ramswaroop.jbot.core.slack.models.Message;

/**
* Base class for making Slack Bots. Any class extending
* this will get all powers of a Slack Bot.
*
* @author ramswaroop
* @version 1.0.0, 05/06/2016
*/
public abstract class Bot {

private static final Logger logger = LoggerFactory.getLogger(Bot.class);
/**
* A Map of all methods annotated with {@link Controller} where key is the {@link EventType#name()} and
* value is a list of {@link MethodWrapper}. NOTE: It does not contain methods which are part of any
* conversation.
*/
private final Map<String, List<MethodWrapper>> eventToMethodsMap = new HashMap<>();
/**
* A Map of all methods annotated with {@link Controller} where key is the {@link Method#getName()} and
* value is the respective {@link MethodWrapper}.
*/
private final Map<String, MethodWrapper> methodNameMap = new HashMap<>();
/**
* A List of names of the methods which are part of any conversation.
*/
private final List<String> conversationMethodNames = new ArrayList<>();
/**
* A List of Queues with each Queue holding all methods for a particular conversation. Methods
* can be chained into a conversation by {@link Controller#next()}.
*/
private final Map<String, Queue<MethodWrapper>> conversationQueueMap = new HashMap<>();

/**
* Service to access Slack APIs.
*/
@Autowired
protected SlackService slackService;

/**
* Class extending this must implement this as it's
* required to make the initial RTM.start() call.
*
* @return
*/
public abstract String getSlackToken();

/**
* An instance of the Bot is required by
* the {@link BotWebSocketHandler} class.
*
* @return
*/
public abstract Bot getSlackBot();

/**
* Construct a map of all the controller methods to handle RTM Events.
*/
public Bot() {
Method[] methods = this.getClass().getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Controller.class)) {
Controller controller = method.getAnnotation(Controller.class);
EventType[] eventTypes = controller.events();
String pattern = controller.pattern();
String next = controller.next();

if (!StringUtils.isEmpty(next)) {
conversationMethodNames.add(next);
}

MethodWrapper methodWrapper = new MethodWrapper();
methodWrapper.setMethod(method);
methodWrapper.setPattern(pattern);
methodWrapper.setNext(next);

if (!conversationMethodNames.contains(method.getName())) {
for (EventType eventType : eventTypes) {
List<MethodWrapper> methodWrappers = eventToMethodsMap.get(eventType.name());

if (methodWrappers == null) {
methodWrappers = new ArrayList<>();
}

methodWrappers.add(methodWrapper);
eventToMethodsMap.put(eventType.name(), methodWrappers);
}
}
methodNameMap.put(method.getName(), methodWrapper);
}
}
}

/**
* Invoked after a successful web socket connection is
* established. You can override this method in the child classes.
*
* @param session
* @see WebSocketHandler#afterConnectionEstablished
*/
public void afterConnectionEstablished(WebSocketSession session) {
logger.debug("WebSocket connected: {}", session);
}

/**
* Invoked after the web socket connection is closed.
* You can override this method in the child classes.
*
* @param session
* @param status
* @see WebSocketHandler#afterConnectionClosed
*/
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
logger.debug("WebSocket closed: {}, Close Status: {}", session, status.toString());
}

/**
* Handle an error from the underlying WebSocket message transport.
*
* @param session
* @param exception
* @see WebSocketHandler#handleTransportError
*/
public void handleTransportError(WebSocketSession session, Throwable exception) {
logger.error("Transport Error: {}", exception);
}

/**
* Invoked when a new Slack event(WebSocket text message) arrives.
*
* @param session
* @param textMessage
* @throws Exception
*/
public final void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {
ObjectMapper mapper = new ObjectMapper();
try {
Event event = mapper.readValue(textMessage.getPayload(), Event.class);
if (event.getType() != null) {
if (event.getType().equalsIgnoreCase(EventType.IM_OPEN.name())
|| event.getType().equalsIgnoreCase(EventType.IM_CREATED.name())) {
if (event.getChannelId() != null) {
slackService.addDmChannel(event.getChannelId());
} else if (event.getChannel() != null) {
slackService.addDmChannel(event.getChannel().getId());
}
} else if (event.getType().equalsIgnoreCase(EventType.MESSAGE.name())) {
if (event.getText() != null && event.getText().contains(slackService.getCurrentUser().getId())) { // direct mention
event.setType(EventType.DIRECT_MENTION.name());
} else if (slackService.getDmChannels().contains(event.getChannelId())) { // direct message
event.setType(EventType.DIRECT_MESSAGE.name());
}
}
} else { // slack does not send any TYPE for acknowledgement messages
event.setType(EventType.ACK.name());
}

if (isConversationOn(event)) {
invokeChainedMethod(session, event);
} else {
invokeMethods(session, event);
}
} catch (Exception e) {
logger.error("Error handling response from Slack: {}. Exception: ", textMessage.getPayload(), e);
}
}

/**
* Call this method to start a conversation.
*
* @param event
*/
public void startConversation(Event event, String methodName) {
String channelId = event.getChannelId();

if (!StringUtils.isEmpty(channelId)) {
Queue<MethodWrapper> queue = formConversationQueue(new LinkedList<>(), methodName);
conversationQueueMap.put(channelId, queue);
}
}

/**
* Call this method to jump to the next method in a conversation.
*
* @param event
*/
public void nextConversation(Event event) {
Queue<MethodWrapper> queue = conversationQueueMap.get(event.getChannelId());
if (queue != null) queue.poll();
}

/**
* Call this method to stop the end the conversation.
*
* @param event
*/
public void stopConversation(Event event) {
conversationQueueMap.remove(event.getChannelId());
}

/**
* Check whether a conversation is up in a particular slack channel.
*
* @param event
* @return true if a conversation is on, false otherwise.
*/
public boolean isConversationOn(Event event) {
return conversationQueueMap.get(event.getChannelId()) != null;
}

/**
* Method to send a reply back to Slack after receiving an {@link Event}.
* Learn <a href="https://api.slack.com/rtm">more on sending responses to Slack.</a>
*
* @param session
* @param event
* @param reply
*/
public final void reply(WebSocketSession session, Event event, Message reply) {
try {
if (StringUtils.isEmpty(reply.getType())) {
reply.setType(EventType.MESSAGE.name().toLowerCase());
}
reply.setText(encode(reply.getText()));
if (reply.getChannel() == null && event.getChannelId() != null) {
reply.setChannel(event.getChannelId());
}
session.sendMessage(new TextMessage(reply.toJSONString()));
if (logger.isDebugEnabled()) { // For debugging purpose only
logger.debug("Reply (Message): {}", reply.toJSONString());
}
} catch (IOException e) {
logger.error("Error sending event: {}. Exception: {}", event.getText(), e.getMessage());
}
}

/**
* Encode the text before sending to Slack.
* Learn <a href="https://api.slack.com/docs/formatting">more on message formatting in Slack</a>
*
* @param message
* @return encoded text.
*/
private String encode(String message) {
return message == null ? null : message.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
}

/**
* Form a Queue with all the methods responsible for a particular conversation.
*
* @param queue
* @param methodName
* @return
*/
private Queue<MethodWrapper> formConversationQueue(Queue<MethodWrapper> queue, String methodName) {
MethodWrapper methodWrapper = methodNameMap.get(methodName);
queue.add(methodWrapper);
if (StringUtils.isEmpty(methodName)) {
return queue;
} else {
return formConversationQueue(queue, methodWrapper.getNext());
}
}

/**
* Invoke the methods with matching {@link Controller#events()}
* and {@link Controller#pattern()} in events received from Slack.
*
* @param session
* @param event
*/
private void invokeMethods(WebSocketSession session, Event event) {
try {
List<MethodWrapper> methodWrappers = eventToMethodsMap.get(event.getType().toUpperCase());
if (methodWrappers == null) return;

methodWrappers = new ArrayList<>(methodWrappers);
MethodWrapper matchedMethod = getMethodWithMatchingPatternAndFilterUnmatchedMethods(event, methodWrappers);
if (matchedMethod != null) {
methodWrappers = new ArrayList<>();
methodWrappers.add(matchedMethod);
}

if (methodWrappers != null) {
for (MethodWrapper methodWrapper : methodWrappers) {
Method method = methodWrapper.getMethod();
if (method.getParameterCount() == 3) {
method.invoke(this, session, event, methodWrapper.getMatcher());
} else {
method.invoke(this, session, event);
}
}
}
} catch (Exception e) {
logger.error("Error invoking controller: ", e);
}
}

/**
* Invoke the appropriate method in a conversation.
*
* @param session
* @param event
*/
private void invokeChainedMethod(WebSocketSession session, Event event) {
Queue<MethodWrapper> queue = conversationQueueMap.get(event.getChannelId());

if (queue != null && !queue.isEmpty()) {
MethodWrapper methodWrapper = queue.peek();

try {
EventType[] eventTypes = methodWrapper.getMethod().getAnnotation(Controller.class).events();
for (EventType eventType : eventTypes) {
if (eventType.name().equals(event.getType().toUpperCase())) {
methodWrapper.getMethod().invoke(this, session, event);
return;
}
}
} catch (Exception e) {
logger.error("Error invoking chained method: ", e);
}
}
}

/**
* Search for a method whose {@link Controller#pattern()} match with the {@link Event#text}
* in events received from Slack and also filter out the methods whose {@link Controller#pattern()} do not
* match with slack message received ({@link Event#text}) for cases where there are no matched methods.
*
* @param event
* @param methodWrappers
* @return the MethodWrapper whose method pattern match with that of the slack message received, {@code null} if no
* such method is found.
*/
private MethodWrapper getMethodWithMatchingPatternAndFilterUnmatchedMethods(Event event, List<MethodWrapper> methodWrappers) {
if (methodWrappers != null) {
Iterator<MethodWrapper> listIterator = methodWrappers.listIterator();

while (listIterator.hasNext()) {
MethodWrapper methodWrapper = listIterator.next();
String pattern = methodWrapper.getPattern();
String text = event.getText();

if (!StringUtils.isEmpty(pattern) && !StringUtils.isEmpty(text)) {
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(text);
if (m.find()) {
methodWrapper.setMatcher(m);
return methodWrapper;
} else {
listIterator.remove(); // remove methods from the original list whose pattern do not match
}
}
}
}
return null;
}

private StandardWebSocketClient client() {
return new StandardWebSocketClient();
}

private BotWebSocketHandler handler() {
return new BotWebSocketHandler(getSlackBot());
}

/**
* Entry point where the web socket connection starts
* and after which your bot becomes live.
*/
@PostConstruct
private void startWebSocketConnection() {
slackService.startRTM(getSlackToken());
if (slackService.getWebSocketUrl() != null) {
WebSocketConnectionManager manager = new WebSocketConnectionManager(client(), handler(), slackService.getWebSocketUrl());
manager.start();
} else {
logger.error("No websocket url returned by Slack.");
}
}

/**
* Wrapper class for methods annotated with {@link Controller}.
*/
private class MethodWrapper {
private Method method;
private String pattern;
private Matcher matcher;
private String next;

public Method getMethod() {
return method;
}

public void setMethod(Method method) {
this.method = method;
}

public String getPattern() {
return pattern;
}

public void setPattern(String pattern) {
this.pattern = pattern;
}

public Matcher getMatcher() {
return matcher;
}

public void setMatcher(Matcher matcher) {
this.matcher = matcher;
}

public String getNext() {
return next;
}

public void setNext(String next) {
this.next = next;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

MethodWrapper that = (MethodWrapper) o;

if (!method.equals(that.method)) return false;
if (pattern != null ? !pattern.equals(that.pattern) : that.pattern != null) return false;
if (matcher != null ? !matcher.equals(that.matcher) : that.matcher != null) return false;
return next != null ? next.equals(that.next) : that.next == null;

}

@Override
public int hashCode() {
int result = method.hashCode();
result = 31 * result + (pattern != null ? pattern.hashCode() : 0);
result = 31 * result + (matcher != null ? matcher.hashCode() : 0);
result = 31 * result + (next != null ? next.hashCode() : 0);
return result;
}
}
}

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote