COIT20256 Assignment 2


COIT20256 Assignment 2 Practical Assessment

Due date:
11:45pm Friday Week 11 Final Submission (in the assignment 2 submission area): The Word document with your report and the code for the finished phase of your assignment in a zip file that was created by exporting the project from NetBeans. Marks will be deducted for late submission.
ASSESSMENT 2
Weighting:
20%



Content

Objectives 2

Background – The Task 2

Phase 1: 4

Phase 2: 6

Nodes in the graph (Rooms) 6

Edges in the graph (Doors) 6

The Graph 6

The loadMap() method 7

Phase 3: 10

Phase 4: 11

Phase 5 16

To be submitted: 17

Marking Criteria 17

Additional Work (for fun – not for the assignment or assessment) 19

A. Organising the classes into packages (and design considerations) 19

B. Experimenting with the graph data structure 19

B. Developing the adventure game further 19

Appendix A: Arrays to build the world map 20

Appendix B: Script to populate the database 23

Objectives

In this assignment, you are required to design, develop and test a software application that employs a Graphical User Interface (GUI) developed using JavaFX and a graph data structure which is configured from a database.

This assignment must be implemented:

  1. incrementally according to the phases specified in this documentation.
  2. according to the design given in these specifications. Make sure your assignment corresponds to the class diagrams and descriptions of the methods and classes given in this document.

Background – The Task

In an adventure game, the user normally explores a world encountering treasure, magical creatures, monsters and obstacles to be overcome, often with an ultimate quest to be achieved.

In this assignment you are going to start the development of a simple screen-based adventure game by adding the functionality to build the layout of the world to be explored and providing the user with the ability to move around and explore this world using simple text-based commands entered through a GUI.

In our adventure the world consists of rooms connected to other rooms through doors. Each room will have a unique id (a number stored as a String), a simple description of the room, a description of items in the room and a list of the doors to other rooms.

In our simple adventure, the user can enter the following basic commands to explore the world:

  • enter n – which allows the user to enter room n, if there is a door to room n from the current location. If there is no door to room n from the current location, enter n results in an error message followed by the description of the current room which includes the list of the rooms that can be accessed through doors from the current location. When the user enters a new room, the user will be told which room has been entered followed by the brief description of the room.
  • search – this provides a more detailed description of the room and any items present in the room.
  • describe – repeats the initial description of the room plus the list of doors to adjacent rooms.
  • goto n – is a magical teleportation command that can take you to any room in the world from any location (assuming that such a room exists). If no such room exists, an error message is displayed.

Future development could have rooms with monsters you have to fight, treasure you can collect, traps, magical creatures that interact with you in some way, maintenance of the user attributes (inventory, strength …) and additional commands to collect treasure, fight monsters etc., but for this assignment we restrict ourselves to the above list of commands. The initial descriptions of the rooms and their more detailed contents is also very simplistic and limited to description 1 for room 1 etc. This is appropriate for testing the application. In an actual game you would have much more imaginative descriptions for your rooms and their contents.

The graphical user interface is to include a TextArea, a TextField, two Labels (Command and the heading) and two Buttons (Help and Exit) and is to appear as follows:

Figure 1: The basic GUI

When you begin the game, a welcome message and details about the first room is displayed as follows:

Figure 2: The GUI when the game starts

The user commands are to be entered in the command text field. For example, entering the command enter 11 followed by the <ENTER> key in the text field as shown below will result in the user entering room 11.

Figure 3 Example of a command being entered

The behavior of the two buttons is as follows:

  • Help button – clicking this button will display the valid commands in the text area.
  • Exit button – clicking this button will exit the game.

As with assignment1, you are to develop this assignment incrementally and get each phase working before progressing to the next.

Phase 1:

Call your application Adventure.

In phase 1 of the assignment you are to use JavaFX and SceneBuilder to develop the GUI described above and to include the code to implement the following behaviours:

  • Clicking the Help Button is to result in the display of the valid commands in the text area (see screenshot below).
  • Clicking the Exit Button is to result in program termination.
  • Entering a command in the “command” text field followed by the <ENTER> key is to display the command that was entered in the text area. In a subsequent phase this action will be replaced with the required behavior for the command that was entered.

Hints:

  1. For the text field specify the action listener name in the same way as for the buttons. When the <ENTER> key is pressed in the text field, an action event will be generated.
  2. Use the selectAll() method with the text Field to make it easier for the user to enter the next command.
  3. Calling System.exit(int) is an acceptable way to exit the JavaFX application. However, it does not allow the Application stop() method to run. Platform.exit() is the preferred way to terminate a JavaFX Application. Either will be acceptable in the assignment. See the API for more details:
https://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html

Remember to give your GUI controls and event handlers meaningful names.

After clicking the help button, you should see something similar to the following:

Figure 4: After clicking Help button

Figure 5: Phase 1 – After entering a command in the command TextField

Phase 2:

Build the graph data structure that corresponds to your “World map”. It will consist of rooms and doors to adjacent rooms.

The graph data structure is to be implemented as follows.

Nodes in the graph (Rooms)

Each node of the graph is to be an instance of a Room class.

The Room class has the following private attributes:

String roomID; //the unique room id represented as a String

String initialDescription; //the initial room description

String searchDescription; //the more detailed description after

//the user searches the room

ArrayList <Door> doors; //a list of all the doors (or graph edges)

//that can be accessed from this room

Edges in the graph (Doors)

Each edge is to be an instance of a Door class.

The Door class has the following private attributes:

String from; // the “from” room – this will be the current room

String to: // the “to” room – this is the room that this door

// will take you to

Note that the doors/edges have a direction. This means that for the door to allow you to go in both directions, both rooms involved must have the door to the other room in their edge list. Note it is possible that some doors are only one-way.

An example of a normal bi-directional door between room 1 and room 11 would mean that

  1. Room 1 will have a door edge in its ArrayList that has a from attribute of “1” and a to attribute of “11”. All the room 1 edges (or doors) will have a from attributre of “1”.
  2. Room 11 will have a door edge in its ArrayList that has a from attribute of “11” and a to attribute of “1”. All the room 11 edges (or doors) will have a from attribute of “11”.

The Graph

We will implement our graph in a World class. This class will have the following private attributes:

HashMap<String, Room> map; // List of nodes (or rooms) in the

// world. The roomID is to be used as

// the key to access the room in the

// HashMap. The rooms will have a

// list of doors(edges) to other rooms

Room currentRoom; //keeps track of the user’s current location

In phase 2 you are to initialize your graph using 4 arrays with the following data:

  1. roomIDs – this array has all the roomIDs
  2. roomDescriptions – this array has the basic description that will be displayed each time the user enters a room
  3. searchDescriptions – this array has the more detailed descriptions of the room and the contents the user will see if they execute the search command. Note that more advanced implementations of the game could have various types of items or entities in the room (e.g. treasure, creatures etc.) and the room contents could change as the user interacts with the game.
  4. Edges – this array will have two entries for every 2-way door indicating the from and to direction of the door/edge

Appendix A has the data you are to use to initialize the graph data structure that represents the map of the adventure world. You can copy this data into your World.java file so that it can be used to build the data structure.

To build the graph, develop the following loadMap() method and invoke it from the World’s constructor.

The loadMap() method

This method is to use the arrays described above to build the graph as follows:

  1. Create a new HashMap and assign it to the World class’s map attribute (the instance variable that was declared as HashMap<String, Room> map discussed above)
  2. Use a for loop to Iterate through the data in the arrays (that contain the the room data), creating a new Room and adding the new room to the HashMap using its roomID as the key to the HashMap.

For example, if you were just to add one room to the HashMap, you would use code similar to the following to create the new Room:

Room r = new Room( roomIDs[0], roomDescriptions[0], searchDescriptions[0] );

And then add this room to the HashMap use roomIDs[0] as the key. (Hint: use map’s put() method)

  1. Process the edges array to add the edges (or doors) to the edge (or doors) list for each of the rooms. For example, the first element in edges is

“1”, “11”

This means that you have to add a door (or an edge) to the list of doors accessible from room “1” that will lead to room “11”.

To add the door to the list of doors for a room:

  • Get a reference to the room (in this example room “1”) from the map (HashMap) you just created. (Hint: use map’s get() method)
  • Use the room’s addDoor() method to add a door to room “11” (i.e. add a door from room “1” to room “11”. The addDoor method is to create a new door (edge) and add it to the room’s list of doors.

The UML diagram for the classes used to build the graph to represent the world:

Figure 6: UML diagram for the graph classes

To complete phase 2:

  1. Complete the methods for World.java, Door.java and Room.java (see UML diagram above). Most of the methods should be self-explanatory. However, to avoid ambiguity, a brief description of three of the methods are given below:
    1. The World’s describe method is to invoke (or call) the current room’s roomDescription method and return the room description in response to a describe command being entered by the user.
    2. The Room’s roomDescription method is to return a String that has the basic room description as well as the list of doors that are accessible from that room. The searchDescription is not returned at this point.
    3. The World’s start method is to set the current room to the room with roomID of “1”.
  2. In the start() method in Adventure.java (i.e. in the file with main() and start()) create an instance of World and display the contents of your graph data structure to check that it has been built correctly. Output the data structure to the standard output using World’s displayMapData() method. In this phase, your start method should be similar to the following:

@Override

public void start(Stage stage) throws Exception

FXMLLoader loader = new

FXMLLoader(getClass().getResource(“FXMLDocument.fxml”));

Parent root = loader.load();

World map = new World();

System.out.println(“Data in Map:”);

map.displayMapData();

Scene scene = new Scene(root);

stage.setScene(scene);

stage.show();

Your output should be similar to the following, but with output for every room:

Phase 3:

The event handlers in the controller will require access to the graph data structure to perform their tasks. This means that you will have to provide access to the data structure in the controller class in the same way that you did for question 3 in the week 4 tutorial.

To achieve this:

  1. In the controller class:
  1. Declare a member variable in the controller that is of type World.
  2. Write a startGame() method which
  1. takes a parameter of type World and assigns it to the member variable in the controller class. This will give the controller access to the data structure with your world map.
  2. calls the map’s start method which will set the current room to the first room.
  3. displays the appropriate messages to indicate the start of the game. It should welcome the user, describe the current room and display the help menu (see screenshot below).Note that only the basic room description and available doors are provided in the initial description when someone enters a room.
  4. In the start() method in Adventure.java (i.e. the file that also contains your main method)
    1. obtain a reference to the controller object
    2. invoke the controller’s startGame() method and pass it the reference to the map of the world (i.e. the graph data structure with the World map). The controller’s startGame() method is described above.

The GUI should appear similar to the following when you start your program.

Phase 7: Initial display when the application starts

Hint: The TextArea control’s appendText method is helpful for achieving the above display of information.

Phase 4:

There are several commands that can be executed by entering the command in the command text field box in the GUI.

We are again going to use polymorphism and inheritance to perform the commands. The UML diagram for the hierarchy for the command classes is given below. Note that some of the commands have an argument (e.g. the room to go to) and others do not (e.g. search which just searches the current room). The super class Command therefore has two constructors. One which will set argument to null and one which is passed the argument to be associated with the command.

In phase 4, you are to implement the hierarchy of commands shown in the following UML diagram:

Figure 8: Class hierarchy for commands

Now when a command is entered in the command text field your application must:

  1. determine if it is a valid command;
  2. If it is a valid command, then create an instance of the appropriate command type;
  3. If it is not a valid command, then create an instance of the UnknownCommand;
  4. Use the command’s perform method to perform the command (polymorphically)

Hints:

  1. You can use a private createCommand() method in the controller to determine the type of command to be created. createCommand should get called from the action listener/event handler that handles a change in the command text field of the GUI. Once createCommand has determined the type of command to be created, it should create an instance of the command and return the command to be performed polymorphically by the event handler (i.e. the new command should be returned to the action listener that that invoked createCommand) . The action listener should then invoke the command’s perform method to perform the command and update the text displayed in the main Text Area of the GUI.
  2. You can split the command String into tokens. (There should be either 0 or 2 tokens depending on whether or not the command takes an argument).

Examples of output from the various commands (including invalid commands) are shown in the screenshots below.

Figure 9: A valid enter command

Figure 10: An invalid enter command (trying to enter a room with no door from current location)

Figure 11: An invalid enter command (no room specified – i.e. argument missing)

Figure 12: A valid describe command

Figure 13: An invalid describe command (describe should not include an argument)

Figure 14: A valid search command

Figure 15: A valid goto command

Figure 16: An invalid goto command (trying to teleport to a non-existent room)

Figure 17: An invalid (unknown) command

Phase 5

In this phase, instead of building the graph to represent the map of the world using the arrays you have copied into the World class, you are to load the data from a database. You must use Apache Derby in this assignment (as you did in the week 9 tutorial).

In this phase you must remove (or comment out) the code you used to build the graph using the arrays.

The database will have two tables. One table (ROOMS) will have all the information about a room (it’s id and both descriptions). The second table (EDGE) will have the information about all the doors accessible from a room. To simplify this task, every room will have 3 doors.

Just as in phase 2, output the data in the graph data structure that was built using the data in the database. Again display your graph data on the standard output using World’s displayMap() method.

The script to build the database is provided in Appendix B.

To be submitted:

  1. Your completed code. You are to submit the exported zip file(s) for your program(s) from NetBeans.

Note that no marks can be awarded for code that does not compile. Code must be developed incrementally in the order of phase development described in this document. You must also code according to the design specified in this document.

In your report make it clear which phase you have completed and thoroughly tested.

  1. A Word document report

A template for your report is provided in the assignment 2 area. You are to complete the following sections:

  1. Description Section: A description of your program describing the phase you have completed (and what it does).
  2. Testing Section: Table to be completed in the template
  3. Bugs/Limitations Section: If there are known “bugs” they must be documented here otherwise you will lose marks for poor testing as well as the bug in your code.

Marking Criteria

This assessment item is worth 20% of the final mark for the unit.

The marks available will depend on which phase you have completed according to the design specified in this document. The phase submitted must show all the functionality required for that phase (and preceding phases) and be thoroughly tested. Marks cannot be awarded for an assignment that does not compile.

Marks will be awarded for the following:

  1. Design and Implementation: The functionality must be complete for the phase you submitted and must be implemented according to the design given in this specification.
  2. Language use: This includes correct usage of inheritance, polymorphism, correct use of the required data structures, variable declarations, constant declarations, class definitions, object creation, loop control, use of selection statements, method definitions and use, display of results etc. Marks will be deducted for unnecessary/very inefficient code. Refer to the “Guidelines for Java Programming” documentation on the unit website for more details.
  3. Documentation: This includes some of the marks under the “layout and code documentation” column where you are awarded marks for appropriate comments in your code. You must have comments before each class and each method. You must also have comments in the body of the code (where appropriate). Comments in the code must be meaningful, correct and useful. Marks will also be deducted under the “layout and code documentation” column if the layout or names of classes, methods and variables does not follow normal conventions.

Documentation marks also include the marks for the accompanying documentation/report. The contents of the report are specified above. A template for the report is also provided in Moodle.

  1. Testing: Part of the marks are for listing the test/checks you performed to make sure each of your completed phases is working correctly. These are to be listed in the report (see the table to be completed in the template). Marks awarded for testing will include your list of tests/checks in the report as well as for working code that passes all the “tests/checks”. Note that the documentation also includes a section for bugs/limitations. Ideally, the phase you submit should not have any bugs.

However, if the phase you submit has any “bugs” (i.e. does not pass all its tests/checks) and you do not document them, you will lose marks for the functionality that does not work and for inadequate testing and documentation.

If the phase you submit does not have the functionality for that phase, you will be marked according to the last working phase your code demonstrates. Make sure you have backed up a complete, thoroughly tested phase ready for submission before you add the functionality for the next phase.

  1. Marks will be deducted for late submission according to the university policy (-5% for each day late).

The available marks for each phase is shown in the following table:


Functionality: implemented incrementally and using the design specified Language Use Testing (thorough testing and documented tests in report)
Report (sections other than test results table) Layout and code documentation
Totals
Phase1 (GUI) /8 /5 /3 /5 /5 /26
Phase 2 (Graph) /8 +/8 =/16 /10 /3+ /2 /5 /10 /46
Phase 3 (StartGame) /8 +/8+/8 =/24 /10 /3 +/2 +/2 /5 /10 /56
Phase 4 (Commands) /32 (i.e. 4*8)
/10 /3 +/2+/2+/5 /5 /10 /69
Phase 5 (database) /40 (i.e. 5*8) /10 /3 +/2+/2+/5+/3 /5 /10 /80
Totals 40 10 15 5 10 80

The assessment item is worth 20% so this mark will be converted to a mark out of 20.

Additional Work (for fun – not for the assignment or assessment)

A. Organising the classes into packages (and design considerations)

Organise your classes into packages. If you use NetBeans when you move a class into a package the code should be automatically refactored. I suggest you use the package structure described in the PowerPoint slides called “Assignment2_packages” in week 12.

B. Experimenting with the graph data structure

Now that you have developed the graph data structure for this application and you have covered recursion, you might like to experiment with the data structure a bit more. Some of the things you might like to explore:

  1. Performing a depth first search of the graph (world)
  2. Performing a breadth first search of the graph (world)
  3. Finding the shortest path between two rooms

You will have to research appropriate algorithms to perform the above tasks and adapt them for your data structure. Note that the algorithms will need you to include some additional information in your nodes and edges (e.g. to record if a room has been visited or not, a distance associated with an edge (all the doors could be a distance of 1 unit between adjacent rooms)).

B. Developing the adventure game further

You may also like to make modifications to the game to make it more interesting. For example you could have additional features in rooms such as traps the user can fall into, additional entities in the game (e.g. monsters the users can interact with, the game player), treasures the user could collect etc. What additional classes would you add to achieve this extra functionality? What changes would you make to the Room to allow for the additional features (treasure, traps, other entities)?

Appendix A: Arrays to build the world map

// data to build the map of the world. nodes are the rooms, edges are the doors

static String[] roomIDs =

“1”,

“2”,

“3”,

“4”,

“5”,

“6”,

“7”,

“8”,

“9”,

“10”,

“11”,

“12”,

“13”,

“14”,

“15”,

“16”,

“17”,

“18”,

“19”,

“20”,;

static String[] roomDescriptions =

“desc1”,

“desc2”,

“desc3”,

“desc4”,

“desc5”,

“desc6”,

“desc7”,

“desc8”,

“desc9”,

“desc10”,

“desc11”,

“desc12”,

“desc13”,

“desc14”,

“desc15”,

“desc16”,

“desc17”,

“desc18”,

“desc19”,

“desc20”,;

static String[] searchDescriptions =

“room1 detailed description of results of search”,

“room2 detailed description of results of search”,

“room3 detailed description of results of search”,

“room4 detailed description of results of search”,

“room5 detailed description of results of search”,

“room6 detailed description of results of search”,

“room7 detailed description of results of search”,

“room8 detailed description of results of search”,

“room9 detailed description of results of search”,

“room10 detailed description of results of search”,

“room11 detailed description of results of search”,

“room12 detailed description of results of search”,

“room13 detailed description of results of search”,

“room14 detailed description of results of search”,

“room15 detailed description of results of search”,

“room16 detailed description of results of search”,

“room17 detailed description of results of search”,

“room18 detailed description of results of search”,

“room19 detailed description of results of search”,

“room20 detailed description of results of search”,;

//edges are the doors: Edge format is <from, to>

static String[][] edges =

“1”, “11”,

“1”, “10”,

“1”, “2”,

“2”, “12”,

“2”, “1”,

“2”, “3”,

“3”, “13”,

“3”, “2”,

“3”, “4”,

“4”, “14”,

“4”, “13”,

“4”, “3”,

“4”, “5”,

“5”, “15”,

“5”, “4”,

“5”, “6”,

“6”, “16”,

“6”, “5”,

“6”, “7”,

“7”, “17”,

“7”, “6”,

“7”, “8”,

“8”, “18”,

“8”, “7”,

“8”, “9”,

“9”, “19”,

“9”, “8”,

“9”, “10”,

“10”, “20”,

“10”, “9”,

“10”, “1”,

“11”, “1”,

“11”, “19”,

“11”, “13”,

“12”, “2”,

“12”, “20”,

“12”, “14”,

“13”, “4”,

“13”, “3”,

“13”, “11”,

“13”, “15”,

“14”, “4”,

“14”, “12”,

“14”, “16”,

“15”, “5”,

“15”, “13”,

“15”, “17”,

“16”, “6”,

“16”, “14”,

“16”, “18”,

“17”, “7”,

“17”, “15”,

“17”, “19”,

“18”, “8”,

“18”, “16”,,

“18”, “20”,,

“19”, “9”,,

“19”, “17”,,

“19”, “11”,,

“20”, “10”,,

“20”, “18”,,

“20”, “12”,;

Appendix B: Script to populate the database

DROP TABLE EDGES;

CREATE TABLE EDGES

(

ROOM0 VARCHAR (8) NOT NULL,

ROOM1 VARCHAR (8) NOT NULL,

ROOM2 VARCHAR (8) NOT NULL,

ROOM3 VARCHAR (8) NOT NULL,

PRIMARY KEY (ROOM0)

);

INSERT INTO EDGES (ROOM0,ROOM1,ROOM2,ROOM3)

VALUES

( ‘1’, ’11’, ’10’, ‘2’ ),

( ‘2’, ’12’, ‘1’, ‘3’ ),

( ‘3’, ’13’, ‘2’, ‘4’ ),

( ‘4’, ’14’, ‘3’, ‘5’ ),

( ‘5’, ’15’, ‘4’, ‘6’ ),

( ‘6’, ’16’, ‘5’, ‘7’ ),

( ‘7’, ’17’, ‘6’, ‘8’ ),

( ‘8’, ’18’, ‘7’, ‘9’ ),

( ‘9’, ’19’, ‘8’, ’10’ ),

( ’10’, ’20’, ‘9’, ‘1’ ),

( ’11’, ‘1’, ’19’, ’13’ ),

( ’12’, ‘2’, ’20’, ’14’ ),

( ’13’, ‘3’, ’11’, ’15’ ),

( ’14’, ‘4’, ’12’, ’16’ ),

( ’15’, ‘5’, ’13’, ’17’ ),

( ’16’, ‘6’, ’14’, ’18’ ),

( ’17’, ‘7’, ’15’, ’19’ ),

( ’18’, ‘8’, ’16’, ’20’ ),

( ’19’, ‘9’, ’17’, ’11’ ),

( ’20’, ’10’, ’18’, ’12’ );

DROP TABLE ROOMS;

CREATE TABLE ROOMS

(

ROOMID VARCHAR (4) NOT NULL,

ROOMDESC VARCHAR (1000) NOT NULL,

SEARCHDESC VARCHAR (1000) NOT NULL,

PRIMARY KEY (ROOMID)

);

INSERT INTO ROOMS (ROOMID,ROOMDESC,SEARCHDESC)

VALUES

( ‘1’, ‘basic description of room 1′,’room 1: detailed description of results of search’),

( ‘2’, ‘basic description of room 2′,’room 2: detailed description of results of search’),

( ‘3’, ‘basic description of room 3′,’room 3: detailed description of results of search’),

( ‘4’, ‘basic description of room 4′,’room 4: detailed description of results of search’),

( ‘5’, ‘basic description of room 5′,’room 5: detailed description of results of search’),

( ‘6’, ‘basic description of room 6′,’room 6: detailed description of results of search’),

( ‘7’, ‘basic description of room 7′,’room 7: detailed description of results of search’),

( ‘8’, ‘basic description of room 8′,’room 8: detailed description of results of search’),

( ‘9’, ‘basic description of room 9′,’room 9: detailed description of results of search’),

( ’10’, ‘basic description of room 10′,’room 10: detailed description of results of search’),

( ’11’, ‘basic description of room 11′,’room 11: detailed description of results of search’),

( ’12’, ‘basic description of room 12′,’room 12: detailed description of results of search’),

( ’13’, ‘basic description of room 13′,’room 13: detailed description of results of search’),

( ’14’, ‘basic description of room 14′,’room 14: detailed description of results of search’),

( ’15’, ‘basic description of room 15′,’room 15: detailed description of results of search’),

( ’16’, ‘basic description of room 16′,’room 16: detailed description of results of search’),

( ’17’, ‘basic description of room 17′,’room 17: detailed description of results of search’),

( ’18’, ‘basic description of room 18′,’room 18: detailed description of results of search’),

( ’19’, ‘basic description of room 19′,’room 19: detailed description of results of search’),

( ’20’, ‘basic description of room 20′,’room 20: detailed description of results of search’);

[Button id=”1″]



Source link

Thanks for installing the Bottom of every post plugin by Corey Salzano. Contact me if you need custom WordPress plugins or website design.

Looking for a Similar Assignment? Our ENL Writers can help. Get your first order at 15% off!

Order

Hi there! Click one of our representatives below and we will get back to you as soon as possible.

Chat with us on WhatsApp
%d bloggers like this: