You can play the classic Battleship game together with Alexa. The objective of the game is to destroy all ships of the opponent.
This document will describe all aspects of the project, including technical descriptions of the internal game process and robot mechanics. Before reading further, I recommend you watch the following video, as it will help you get a better understanding of the concepts that will be discussed here.
The game is composed of two boards:
- The upper board will be called Alexa Board, and will be used to manage the status of all attacks and hits executed by the player.
- The bottom board will be called Player Board, and will be used to position the player’s ships. Here, the player can manage the status of all attacks and hits executed by Alexa.
The two boards are composed by nine rows identified by letters from A to I, and nine columns identified by numbers from 1 to 9. At the beginning of the game the player must position the following ships::
This board represent at any moment in time the current status of all attacks and hits executed by the player, impacting Alexa’s ships.
At the beginning of the game this board is empty, as the player has no information on Alexa’s ships..
At the start of the game, the player must complete all zones using blue Duplo bricks to represent the sea, as shown below:.
The following process is used to manage the status of each of the player’s attacks:
- The player informs the zone coordinate the next attack will target.
- Alexa processes the request and informs the final result of the attack (it can either be a hit or a miss).
- In case of a hit, the player must update the board to identify the impact on a ship.
The player can update the status of the Alexa board using a red Duplo brick to identify the impact on a ship.
The board game use three large motors, one medium motor and two touch sensors to control the execution of each attack.
On the left and right side of the board, a large motor is used to control the vertical movement.
A touch sensor is used to control the minimum descent of these motors. The touch sensor is located at the bottom of the board. This sensor is responsible to stop the execution of both motors in order to control the original position for a new attack.
The following code is used to manage this process:
def restart_vertical(tank_pair, tsVertical):
tank_pair.on(left_speed=-40, right_speed=-40)
tsVertical.wait_for_pressed()
tank_pair.stop()
tank_pair.position = 0
sleep(0.5)
- The 'tank_pair' manages both motors at the same time using a 'MoveTank' type.
- The 'tsVertical' manages the touch sensor using a 'TouchSensor' type.
- Both motors are activated at the same time using the same velocity.
- The instruction 'wait_for_pressed' stops the execution of the logic (move to the next line) until it confirms the touch sensor is pressed. After that, the execution resumes at the next line.
- The execution of the large motor is stopped.
- The initial position is defined to zero.
The other large motor is used to move the medium motor from 1 to 9 column zones (horizontal movement). Also, a medium motor is used to push the blue Duplo brick on to zone to be attacked by the player.
The medium motor use a 'Gear Rack 1 x 13 with Axle and Pin Holes' Lego piece to push the Duplo brick.
A touch sensor is used to control the return of the large motor to the original position.
The following code is used to manage this process:
def restart_horizontal(lm, tsHorizontal):
lm.on(speed=-40)
tsHorizontal.wait_for_pressed()
lm.stop()
sleep(0.5)
- The 'lm' parameter manages the large motor using a 'LargeMotor' type.
- The 'tsHorizontal' manages the touch sensor using a 'TouchSensor' type.
- The large motor is activated.
- The instruction 'wait_for_pressed' stops the execution of the logic (move to the next line) until it confirms the touch sensor is pressed. After that, the execution resumes at the next line.
- The execution of the large motor is stopped.
This board represent at any moment in time the current status of all attacks and hits executed by Alexa, impacting the player’s ships.
At the beginning of the game this board is empty.
The player must complete the board with all strategic positions for his ships, and the remaining empty zones with blue Duplo bricks to represent the sea.
The following process is used to manage the status of each of Alexa’s attacks:
- Alexa will inform the zone or coordinate targeted by the attack
- The player must reply 'impact' if the attack hits a ship or 'you fail' in case it misses.
- The player must update the board to identify an impact on a ship.
The next element must be used by the player to identify an impact on a ship:
The game is played by providing verbal commands to Alexa.
Here’s the list of available instructions to be used throughout the game:
- ALEXA OPEN BATTLESHIP GAME: This instruction requests to Alexa to start the program.
- START GAME: This instruction requests Alexa to prepare the board (restore the motors to the initial position) and create a new game.
- <LETTER_ROW> <NUMBER_COLUMN>: This instruction requests Alexa to process an attack and inform the final result (hit or miss).
- YOUR TURN: This instruction informs Alexa that it is now her turn to attack.
- YOU FAIL: Informs Alexa that her attack missed.
- IMPACT: Informs Alexa that her attack hit a ship.
- YOU WIN THE GAME: Informs Alexa that she won the game.
The game instructions are also included on a paper display at the right side of the board.
The flow of the game is the following:
The logic of the game is composed by two parts:
- Alexa skill: Has all the logic to create a new game and process the attack.
- EV3: Has all the logic to execute the player’s attack on the Alexa board.
The list of 'Intents' to process the instructions sent by the player to Alexa are the following:
- StartGameIntent: Responsible for requesting the preparation and creation of a new game.
- PlayerAttackIntent: Responsible for sending the row and column information requested by the player to execute the attack on the Alexa board.
- AlexaAttackIntent: Responsible for processing and generating a new attack on the Player board.
- YouFailAlexaIntent: Responsible for processing a failed attack executed on the Player board.
- ImpactAlexaIntent: Responsible for processing a successful attack executed on the Player board.
- AlexaWinsTheGameIntent: Executed when the player informs Alexa that she has won the game.
All this logic works together with the EV3 logic to manage the status of the game on the robot.
Time SynchronizationA key concept on the logic of this game is time synchronization, because every response of the skill will execute two processes in parallel. One process will be the verbal response from Alexa, and at the same time EV3 will execute the instructions requested by the skill.
Time synchronization is used so that both processes can be managed as if they were a single process (for example, EV3 pushes the blue Duplo block on the board while at the same time Alexa emits a shooting sound).
All attack responses informed by Alexa are composed of three sections:
- Process to move the motors to the row and column requested by the player.
- Process to push the blue Duplo brick.
- Process to return the motors to the original position.
The only process with a constant time (4 seconds) is the logic to push the Duplo brick. The time of the other two processes will depend on the zone where the attack is executed.
The first concepts to review are the velocity and degrees to move the motors (horizontal and vertical) to any zone on the board. The function used on the EV3 code to move the motors is run_to_rel_pos.
run_to_rel_pos
(**kwargs):Run to a position relative to the current position value. The new position will be current position + position_sp. When the new position is reached, the motor will stop using the action specified by stop_action.
The parameters used to control the movement of the motors are position_sp and speed_sp.
- position_sp: Degrees.
- speed_sp: Degrees per second.
The following code provides the logic to move the motors on the EV3:
def move_positon_player_attack(tank_pair, lm, row, column):
# 1 - Move Tank_Pair to the row requested (the initial position is 0)
rowPosition = 8 - row
if(rowPosition > 0):
rowPosition = rowPosition * 140
print("Row : {}".format(rowPosition))
startMoveToPosition = datetime.now()
tank_pair.run_to_rel_pos(position_sp=rowPosition, speed_sp=400)
tank_pair.wait_while(Motor.STATE_RUNNING)
tank_pair.stop()
totalSeconds = total_time_to_move(startMoveToPosition)
print("Total time for row : {}".format(totalSeconds))
sleep(1)
# 2 - Movbe lm to the column requested (the initial position is 8)
columnPosition = 8 - column
if(columnPosition > 0):
columnPosition = columnPosition * 280
print("Column : {}".format(columnPosition))
startMoveToPosition = datetime.now()
lm.run_to_rel_pos(position_sp=columnPosition, speed_sp=400)
lm.wait_while(Motor.STATE_RUNNING)
lm.stop()
totalSeconds = total_time_to_move(startMoveToPosition)
print("Total time for column : {}".format(totalSeconds))
sleep(1)
The main objective of this logic is to put the Medium Motor (responsible for pushing the Duplo brick) at the center of any zone to attack. This logic represents the zones to attack as a multidimensional array with initial zone [ 0, 0 ] (from the player perspective the initial zone is [ I, 9 ]).
The formula to move the vertical motors is:
- TOTAL_NUMBER_OF_DEGREES = (8 - ROW_POSITION) * 140 degrees
The idea of this logic is to move the motors from the original position (in this case it’s ‘0’ from the array perspective, but ‘I’ from the board perspective) to the row position requested by the player. This is why the logic first calculates 8 - ROW_POSITION to find the correct position on the array to reflect the movement on the board.
The next step is to multiply this number by a degree movement factor per zone. The value of this factor is 140 degrees, because it is the number of degrees to put the vertical motors in the middle of a zone.
The final instruction to move the vertical motor is:
- run_to_rel_pos(position_sp = TOTAL_NUMBER_OF_DEGREES, speed_sp = 400).
It uses the total number of degrees calculated on the previous logic.
The logic to move the horizontal motor is similar to the vertical logic, but in this case the degree movement factor to be used is 280 degrees.
The most important factor in both calculations is the value of speed_sp, because this value is related with the total time to be used on the Alexa skill response. Any change on this value will impact the logic created in the AttackBoardTime class used on the skill.
The next step is to execute the logic to push the Duplo brick in the zone requested by the player.
def execute_player_attack(mm, attackResult, shipDestroyed):
# 1 - Push the duplo brick on the board
print("push duplo brick")
mm.run_to_rel_pos(position_sp=-350, speed_sp=400)
mm.wait_while(Motor.STATE_RUNNING)
mm.run_to_rel_pos(position_sp=350, speed_sp=400)
mm.wait_while(Motor.STATE_RUNNING)
mm.stop()
The value of the 'position_sp' is related with length of the Lego piece used to push the brick.
The final step is to return all motors to the original position (zone [ I, 9 ] on the board).
The logic to perform this action is the following:
def restart_vertical(tank_pair, tsVertical):
print("Restart vertical motors. Touch pressed : {}".
format(tsVertical.is_pressed))
tank_pair.on(left_speed=-40, right_speed=-40)
tsVertical.wait_for_pressed()
tank_pair.stop()
tank_pair.position = 0
sleep(0.5)
def restart_horizontal(lm, tsHorizontal):
print("Restart horizontal motor. Touch pressed : {}".
format(tsHorizontal.is_pressed))
lm.on(speed=-40)
tsHorizontal.wait_for_pressed()
lm.stop()
sleep(0.5)
In this case the robot uses a touch sensor to control the final movement to the original position.
The timing to execute these functions must be synchronized with the final response sent by the Alexa skill to give the player the sensation that Alexa controls every action of the game with sounds and verbal commands. The timing logic is defined on the AttackBoardTime code.
The following code on Alexa skill defines the logic to control these timings:
public class AttackBoardTime {
private static Logger log = getLogger(AttackBoardTime.class);
private static final int MIN_COLUMN_NUMBER = 0;
private static final int MAX_COLUMN_NUMBER = 8;
private static final int MAX_ROW_NUMBER = 8;
private static final int DISTANCE_TIME_FACTOR = 700;
private static final int MOVEMENT_FACTOR = 1200;
private static final int SHOOT_TIME = 4000;
private static final int RETURN_VELOCITY_TIME = 4000;
private static final int SHOOT_SOUND_TIME = 3680;
private static final int MAX_BREAK_TIME = 10000;
public static String getSsmlBreaksOnMillisecondsBeforeToAttack(int row,
int column){
int totalTime = getTotalMovementTime(row, column);
log.info("Zone : [{} , {}] - Time before to attack : {}", row,
column, totalTime);
return getTotalPauseInMilliseconds(totalTime, false);
}
public static String getSsmlBreaksOnMillisecondsAfterToAttack(int row,
int column){
int totalTime = getTotalMovementTime(row, column) + SHOOT_TIME
- RETURN_VELOCITY_TIME;
log.info("Zone : [{} , {}] - Time after to attack : {}", row,
column, totalTime);
return getTotalPauseInMilliseconds(totalTime, true);
}
private static int getTotalMovementTime(int row, int column){
int columnTime = (MAX_COLUMN_NUMBER - column) * DISTANCE_TIME_FACTOR;
int rowTime = (MAX_ROW_NUMBER - row) * DISTANCE_TIME_FACTOR;
int totalTime = columnTime + rowTime;
if(row > MIN_COLUMN_NUMBER && column < MAX_COLUMN_NUMBER){
totalTime = totalTime + MOVEMENT_FACTOR;
}
return totalTime;
}
private static String getTotalPauseInMilliseconds(int totalPause,
boolean withSound) {
if(withSound){
totalPause = totalPause - SHOOT_SOUND_TIME;
}
if(totalPause < 0) {
return "<break time=\"0s\"/>";
}
else{
StringBuilder breaks = new StringBuilder();
int greaterThanTen = totalPause / MAX_BREAK_TIME;
int lessThanTen = totalPause % MAX_BREAK_TIME;
log.info("greaterThanTen : {} - lessThanTen : {}",
greaterThanTen, lessThanTen);
for(int i = greaterThanTen; i > 0; i--){
int value = MAX_BREAK_TIME;
breaks.append("<break time=\"" + value + "ms\"/>");
}
if(lessThanTen > 0){
breaks.append("<break time=\"" + lessThanTen + "ms\"/>");
}
return breaks.toString();
}
}
}
This logic has the responsibility to introduce a pause using the break instruction if total time to move the motors form the original position to the final position is greater than 10 seconds. The next image has a description of the timing factor used to perform this calculation:
The timing factor per zone is defined as 700 milliseconds (this factor is related with the speed_sp value used to move the motors on the EV3). It also uses an extra initial factor (movement factor) with 1200 milliseconds in case of a horizontal movement. Other timings to take into consideration are the Shoot Time of 4000 milliseconds and Shoot Sound Time of 3680 milliseconds, because both times must be subtracted from the total time in order to return the motors to the original position.
Logic to manage the Alexa BoardAll logic to manage the status of the game (create a new game, execute attacks, control the status of the player board and the Alexa board, etc) is performed by four classes on the Alexa skill:
- Game
- Ship
- BoardGame
- Zone
The main class is Game, which controls any new game. The attributes of this class are:
public class Game {
private static final int[] LENGTH_SHIPS = {1, 1, 1, 1, 2, 2, 2, 3, 3, 4};
private static final int NUMBER_OF_SHIPS = 10;
public Ship[] ships;
public BoardGame boardGameAlexa;
public BoardGame boardGamePlayer;
This class has the control of all Ships with his own strategic position created by Alexa and status of both boards (Alexa and player) to calculate the zone to attack.
The attributes of the Ship class are the following:
public class Ship {
private int identifier;
private int row;
private int column;
private int length;
private int direction;
private String typeOfShip;
private int lifePoints;
The Ship class has all information about the zone (row and column position), length of ship, direction (vertical or horizontal) and number of impacts (lifePoints).
The attributes of BoardGame class are the following:
public class BoardGame {
private Zone[][] zones;
private int points;
public static final int NUMBER_OF_ROWS = 9;
public static final int NUMBER_OF_COLUMNS = 9;
private final int TOTAL_POINTS_TO_WIN = 20;
This class controls both boards (Player and Alexa) and the total points (number of impacts) to determine the winner of the game.
The attributes of Zone class are the following:
public class Zone {
public static final int ATTACKED = 0;
public static final int IMPACT = 1;
public static final int FAIL = 2;
private int identifyShip;
private boolean hasShip;
private int status;
private int shipLength;
This class controls the type of ship in the zone (identifyShip and hasShip), status of the attack (pending to attack, impact or fail) and ship length.
All these classes have the logic to manage the status of the game. When a new game is started, Alexa skill will generate the game and save the status of the game in the session.
The following code demonstrates this logic:
public Optional<Response> procesarHandle(HandlerInput handlerInput,
IntentRequest intentRequest) {
log.debug("Executing StartGameHandler");
Game game = new BattleshipGame().startGame();
log.info("A new game was created");
String jsonGame = MindstormAlexaUtil.gameToJson(game);
log.debug("JSON with game information created");
MindstormAlexaUtil.putSessionAttribute(handlerInput, "jsonGame",
jsonGame);
log.debug("JSON with game information saved on the session with key
'jsonGame'");
The idea of this logic is:
- Create a new game.
- Determine the strategic positions for the Alexa ships.
- Transform the status of the game (Game class) in a JSON.
- Add the JSON information on the Alexa session with a jsonGame key.
After that, the logic on the Alexa skill (execute a new attack for example) is:
- Get the JSON information form the session using the jsonGame key.
- Restore the game status using the JSON information.
- Perform the attack and update the status of the game.
- Transform the status of the game (Game class) to JSON.
- Add the JSON information on the Alexa session with a jsonGame key.
The following code is demonstrates this logic:
String jsonGame = (String)
MindstormAlexaUtil.getValueFromSession(handlerInput, "jsonGame");
log.debug("Get game status from session");
Game game = MindstormAlexaUtil.jsonToGame(jsonGame);
log.debug("Game status restored form JSON");
log.debug("Execute player attack");
SendCommandDirective payload = new BattleshipGame().
executePlayerAttack(game, rowRequest, column);
jsonGame = MindstormAlexaUtil.gameToJson(game);
log.debug("New game status created from JSON information");
MindstormAlexaUtil.putSessionAttribute(handlerInput, "jsonGame", jsonGame);
log.debug("New game status saved on the session with key 'jsonGame'");
Communication between Alexa and EV3 using DirectiveThe process to send instructions from Lambda functions to EV3 is using 'Directive'. All instructions must be defined on the payload of the Directive as a JSON message. The class 'SendComandDirective' is used to build the payload for any instruction. The next code is an example of a directive sent from 'StartGameHandler' to EV3 after to execute the 'START GAME' request.
SendCommandDirective payload = new SendCommandDirective("command",
"start_game");
return handlerInput.getResponseBuilder()
.withSpeech(i18n(locale, "startGameSpeechText",
"<audio src=\"soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_intro_01\"/>"))
.withReprompt(i18n(locale, "reprompt"))
.withShouldEndSession(false)
.addDirective(MindstormAlexaUtil.buildDirective(endpointId,
MindstormAlexaUtil.NAMESPACE,
MindstormAlexaUtil.NAME_CONTROL,
payload))
.build();
}
On the previous example the function 'StartGameHandler' send a Directive with type value 'start_game'. The logic on the EV3 is responsible to identify the type of directive and execute the logic. For example:
def on_custom_mindstorms_gadget_control(self, directive):
try:
print("***********************************************")
payload = json.loads(directive.payload.decode("utf-8"))
print("Directive - Payload information : {}".format(payload))
print("***********************************************")
command_type = payload["command"]
if command_type == "start_game":
print("Starting a new game")
restart_horizontal(self.lm, self.tsHorizontal)
restart_vertical(self.tank_pair, self.tsVertical)
elif command_type == "player_attack":
The directive with type 'start_comand' is executed on the EV3. The list of directives available on the EV3 logic are the next:
- start_game: Execute the logic to prepare the board (reset motors to the original position) before to start a new game.
- player_attack: Execute the attack requested by the player on the board moving the motors to the requested position (Row, Column)
- alexa_attack: Print by console the attack executed by Alexa
- test_mode: List all strategic position of Alexa ships.
- finish_game: Print by console the message.
According with the documentation 'Speech Synthesis Markup Language, or SSML, is a standardized markup language that allows developers to control pronunciation, intonation, timing, and emotion'. Battleship game use SSML to give the sensation Alexa really feels the emotion of game at the moment to impact a ship, fails an attack or be impacted by an attack of a player.
For example to inform an impact of an attack with an excited pronunciation we use the next configuration:
- Executing attack. <amazon:emotion name="excited" intensity="high">You have impacted a ship!. </amazon:emotion><say-as interpret-as="interjection">well done</say-as>. Attack finished.
Or to inform an impact, but with a disappoint pronunciation we use the next configuration:
- Executing attack. <amazon:emotion name="disappointed" intensity="high">Your shot damaged my ship.</amazon:emotion> <say-as interpret-as="interjection">no</say-as>. Attack finished.
SSML is an amazing feature on Alexa, because gives the player a more realistic experience of the game.
You can create and customize your own responses on the game, because all these text are externalized on properties files files to enable Multi-Language support.
Multi-Language supportAlexa skill code was developed to support multiple languages. At this moment the game has support for English and Spanish. The following video is an example of the game in Spanish:
For this reason the name of the intent created on the new skill is important, because it is used on the handler logic.
public class I18NUtil {
public static final Logger log = LoggerFactory.getLogger(I18NUtil.class);
public static final String BUNDLENAME = "translations";
public static final Locale DEFAULT_LOCALE = new Locale("en", "US");
public static Locale getLocale(HandlerInput input) {
String localeStr = input.getRequest().getLocale();
Locale locale = Locale.forLanguageTag(localeStr);
return locale;
}
public static String i18n(Locale locale, String key, String... parameters)
{
if (locale == null) {
locale = DEFAULT_LOCALE;
}
String i18nMessage = "";
try {
ResourceBundle labels =
ResourceBundle.getBundle("i18n." + BUNDLENAME, locale);
MessageFormat formatter = new MessageFormat("");
formatter.setLocale(locale);
formatter.applyPattern(labels.getString(key));
i18nMessage = formatter.format(parameters);
}
catch (Exception e) {
i18nMessage = key;
}
return i18nMessage;
}
}
This class has two responsibilities:
- Detect the locale used by Alexa from the 'HandlerInput' instance.
- Get the text from a property file using the locale information.
The steps to add a new language support on the robot are the next:
- Validate the locale for the new language support on Alexa documentation
- Open the skill on Alexa Developer Console and select 'Language Settings'
- Click on 'Add new language'
- Choose a new language to add (for example portuguese) and press 'Save' button
- Select the new language form the list
- Click on 'CUSTOM'
- The new language is ready to be configured
- The next step is configure the new language using other language as a template (English for example).
- One important thing is the name of the intent, because it is used on the Lambda code to connect the intent with a handler.
The next part is add support for the new language on the Java code. The steps are the next:
- Create a new 'translation_xx.properties' file and replace the 'xx' with the new local configuration according with Alexa documentation.
The property file is composed by two parts:
- Commons: All text used on basic intents
- Game: All text used on custom intents
The name of the intent created on the Skill is important, because it is used on all handlers in the java code. For example:
public class StartGameHandler extends BattleshipHandler {
public boolean validarHandle(HandlerInput handlerInput,
IntentRequest intentRequest) {
return handlerInput.matches(requestType(IntentRequest.class).
and(intentName(i18n(locale, "startGameIntent"))));
}
The method 'i18n' is used to get the name of the intent from the property file according with the 'locale' detected in the handler. On this case it use the 'startGameIntent' key to get the value from the property file. The information for this handler on the property file is the next:
#################################################
# Game
# 1 - StartGameHandler
startGameIntent=StartGameIntent
For this reason the name of the intent created on the new skill is importan, because it is used on the handler logic.
Building instructionsThe robot was created using parts from Lego Technic, Lego System and Lego Duplo.
The list of parts required to build this robot is the following:
Also, the following parts from a EV3 set are required:
The robot use five 1 x 50 cm/20 in cables.
The following videos describe step by step the building process to create the robot:
EV3 cable connectionsBattleship game use six port connections on the EV3. The following image shows these connections:
- Large Motor vertical movement (left on the picture): Port D
- Large Motor vertical movement (right on the picture): Port A
- Large Motor horizontal movement: Port C
- Medium motor: Port B
- Touch Sensor horizontal movement: Port 1
- Touch Sensor vertical movement: Port 4
All these configurations are available on the BattleshipGame.py file:
def __init__(self):
super().__init__()
# Gadget state
self.patrol_mode = False
# Ev3dev initialization
self.leds = Leds()
self.sound = Sound()
self.tsVertical = TouchSensor(INPUT_4)
self.tsHorizontal = TouchSensor(INPUT_1)
self.mm = MediumMotor(OUTPUT_B)
self.tank_pair = MoveTank(OUTPUT_A, OUTPUT_D)
self.tank_pair.reset()
self.lm = LargeMotor(OUTPUT_C)
Instructions to run the codeThe process to run the code for Battleship game is managed on three different parts:
- EV3: Execute Python code.
- Alexa Developer Console: Manage Alexa Skill logic.
- Amazon Console - Lambda: Manage the backend of Alexa Skill logic.
I would like recommend you to follow and execute all missions created for 'LEGO MINDSTORMS Voice Challenge', because it will facilitate the process to configure and execute the EV3 and Alexa Developer Console logic for Battleship robot. All instructions are available on the next link.
The new instructions to complete the configuration of the robot are created using the Lambda project on Amazon to build the code and upload the binary. There is a great tutorial created by Sergey Smolnikov available on this linkthat shows how to create the instructions to manage and configure Battleship code on AWS.
- Login and open the Developer Console, navigate to Alexa section
- Click on 'Alexa' link under 'amazon alexa' image. After that click on 'Skill Builders --> Developer Console'
- Click on 'Create Skill' button.
- Insert the name of the skill and press 'Create Skill' button.
- Press 'Choose' button to complete the creation of the skill.
- Click on 'JSON Editor'
- Copy and paste the code from 'Battleship_en.json' code available on 'battleship-alexa' project.
- Press 'Save Model' button and confirm the new list of 'Intents' available in the left side of the screen.
- Click on 'Interfaces', enable 'Custom Interface Controller' option and press 'Save Interfaces' button
- Click on 'Endpoints', select 'AWS Lambda ARN', click on 'copy to clipboard' and save this information, as it will be used in the next steps.
- Open a new tab in the browser and login on AWS Management Console
- On 'Find Services' option type 'Lambda' and select this option
- Click on 'Create Function' button
- Add a new to the function in the option 'Function Name', select 'Java 8' as a runtime and press 'Create Function' button.
- In the handler option replace the current value with 'jose.coronel.cl.battleship.BatallaNavalAlexaSkill::handleRequest' and press 'Save' button.
- Click on 'Designer'
- Click on 'Add trigger' and select 'Alexa Skills Kit'
- Add the 'Skill ID' saved during the creation of skill and press 'Add' button
- Click on the ARN information (in the right corner of the screen) and save this information.
- Open the tab with the Alexa Skill Console, add the ARN on the 'Default Region' information and press 'Save Endpoint' button.
- Open the 'AWS Management Console' tab
- We will useIntelliJto build the Java project. Open the tool and select 'Get from Version Control'.
- Select 'GitHub' option, put the 'https://github.com/jcoronelcortes/battleship-alexa.git' value and press 'Clone' button.
- On the right side select 'Maven' option, press the icon with the M letter, introduce the text 'clean package' and press 'Enter' button
- A new folder with 'target' name will be created. Inside of this folder IntelliJ will create a file with name 'alexa-batlla-naval-1.0.0.jar'.
- Open the 'AWS Management Console' tab, press the 'Upload' button, select the new jar file created and press the 'Open' button.
- Click on 'Save' button
- Click on 'Select a test event' list and click on 'Configure test events'
- Select the option 'Amazon Alexa Start Session', add a name and press 'Create' button.
- Click on 'Test' button and confirm the execution of the test.
- Open Alexa Skill Developer tab, click on 'Intent' option and click on 'Build Model' button.
- Click on 'Test' option in the menu and select 'Development' option form the list
- Type 'alexa open battleship game', press 'Enter' button and confirm the next message as a response.
- Congratulations!!!! You have finished the configuration of the code on Alexa Skill and Lambda functions.
Building the robot is not required in order to test the code. In fact I recommend you test the game on a 'Dummy mode' first, in order to understand the logic of the game and code before building the robot.
The steps to test the code are:
- Install and configure the EV3 with EV3DEV.
- Install and configure Visual Studio Code.
- Connect motors and sensors on the EV3 in the right port for each element.
- Create Alexa Skills and Lambda function.
- Execute the code on EV3.
- Use Alexa to start a new game.
- During the execution of the program push touch sensor manually to simulate this behavior.
Battleship project has a lot of pending enhancements and new features to be added. My next steps are:
- New medium level of difficulty: I have completed the logic of Alexa to improve the attack according with the last impact. The code is under test, because I must create a new intent (DESTROYED) and change the timing logic.
- Improve the horizontal large motor movement: It is under test at this moment
- Reduce the vibration of the structure
- Increase the velocity of large motors (vertical): It is under test at this moment, because this change impact the logic of the timing for the game
- Remove the instruction'It is your turn to attack': The idea is include Alexa's attack information at the end of the speech for player response attack.
- Building instructions: Create a professional building instructions following Nico71's recommendations.
- Implement a multiplayer game: The idea is implement a logic to do a match for two player at the same time.
Comments