{"id":20365608,"url":"https://github.com/hackyourfuture/robotapp","last_synced_at":"2025-08-30T15:15:53.556Z","repository":{"id":74102009,"uuid":"117376308","full_name":"HackYourFuture/RobotApp","owner":"HackYourFuture","description":null,"archived":false,"fork":false,"pushed_at":"2018-09-22T11:41:10.000Z","size":193,"stargazers_count":2,"open_issues_count":0,"forks_count":3,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-06-02T11:54:30.650Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HackYourFuture.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-01-13T20:35:56.000Z","updated_at":"2018-11-30T20:35:27.000Z","dependencies_parsed_at":"2023-04-09T18:47:07.009Z","dependency_job_id":null,"html_url":"https://github.com/HackYourFuture/RobotApp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/HackYourFuture/RobotApp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackYourFuture%2FRobotApp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackYourFuture%2FRobotApp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackYourFuture%2FRobotApp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackYourFuture%2FRobotApp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HackYourFuture","download_url":"https://codeload.github.com/HackYourFuture/RobotApp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackYourFuture%2FRobotApp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272866016,"owners_count":25006310,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-30T02:00:09.474Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T00:19:03.669Z","updated_at":"2025-08-30T15:15:53.550Z","avatar_url":"https://github.com/HackYourFuture.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RobotApp\n\n## Introduction\n\nThis project was inspired by the [RoverJS](http://roverjs.com/) app developed by HYF teacher @joostlubach:\n\n\u003e https://roverjs.com/\n\u003e \n\u003e_Hello! I'm Rover, I'm a robot. You can program me through JavaScript._\n\u003e\n\u003e![RoverJS Board](./assets/roverjs.png)\n\u003e\n\u003e _I understand two commands, which are JavaScript functions:_\n\u003e\n\u003e - `move()`\n\u003e - `turn(direction)`\n\u003e \n\u003e _The second function takes one argument, which should be the direction to turn to ('left' or 'right')._\n\u003e\n\u003e _I cannot walk through obstacles (trees, water). Can you get me to the flag?_\n\u003e\n\nThe objective of the project in this repository is to write a JavaScript application that rebuilds the RoverJS game board and adds ways to direct the robot towards the flag, using buttons or commands rather than through user-provided JavaScript functions. This is done step-wise through eight different, progressively more advanced versions of the application.\n\n## Robot State\n\nIn the robot code the state of the game is kept in a number of variables.\nThe state consist of:\n\n| Variable      | Description                                                     |\n| ------------- | --------------------------------------------------------------- |\n| `board`       | A two-dimensional array representing the board.                 |\n| `robot`       | The `x` and `y` coordinates and `dir` (direction) of the robot. |\n| `moves`       | The number of moves made so far.                                |\n| `turns`       | The number of turns made so far.                                |\n| `flagReached` | A boolean indicating whether the flag has been reached.         |\n\n\nThis state is updated as the robot moves and turns. After each state change the game board is rerendered.\n\n```js\nconst board = [\n  ['T', 'T', '.', 'F'],\n  ['T', '.', '.', '.'],\n  ['.', '.', '.', '.'],\n  ['R', '.', '.', 'W']\n];\n\nconst robot = {\n  x: 0,\n  y: 0,\n  dir: 'up'\n};\n\nlet moves = 0;\nlet turns = 0;\nlet flagReached = false;\n```\n\n\u003eNote that the rows in the board array are defined in the order as they appear visually on the page, where the origin (0, 0) is in the lower left-hand corner. To make the origin (0, 0) correspond to the array indices [0, 0] we need to reverse the rows at the start of the application.\n\n## Turning the robot\n\nThe robot can turn `left` and `right`. The new direction of the robot (indicated by the 'antenna' on its head) depends on its current direction (`up`, `down`, `left`, `right`) and the turn direction. This is illustrated in the picture below.\n\n![Turning the robot](./assets/robot-turn.png)\n\nThe following code snippet uses a `switch` statement and [conditional (ternary) operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) to establish the necessary state change of the robot's direction (or should we say _'heading'_ :grinning:).\n\n```js\nfunction turn(turnDirection) {\n  if (turnDirection !== 'left' \u0026\u0026 turnDirection !== 'right') {\n    console.log('ignoring invalid turn', turnDirection);\n  }\n\n  switch (robot.dir) {\n    case 'up':\n      robot.dir = turnDirection === 'left' ? 'left' : 'right';\n      break;\n    case 'down':\n      robot.dir = turnDirection === 'left' ? 'right' : 'left';\n      break;\n    case 'left':\n      robot.dir = turnDirection === 'left' ? 'down' : 'up';\n      break;\n    case 'right':\n      robot.dir = turnDirection === 'left' ? 'up' : 'down';\n      break;\n  }\n\n  turns += 1;\n}\n```\n\nThe conditional operator is a convenient short-hand for the alternative of using `if` statements. Its syntax is:\n\n\u003e _condition_ ? _expr1_ : _expr2_\n\nIf the condition is `true` the resulting value is `expr1`, otherwise it's `expr2`.\n\nFor more details, please refer to [MDN web docs, Conditional (ternary) Operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator).\n\nIn the code snippet above we are testing whether `turnDirection === 'left'` in the knowledge that if it isn't `left` it must be `right`. Alternatively we could have tested for `turnDirection === 'right'` and swapped the two expressions in each of the conditional operators.\n\n## Moving the robot\n\nMoving the robot means adding or subtracting `1` from either its `y` (vertical) position or its `x` (horizontal) position, depending on the current direction of the robot. As a complicating factor, we also must ensure that the robot doesn't 'fall off the board'. In the code snippet below we again use conditional operators to ensure the `x` and `y` values stay within the range of the `board` array.\n\n```js\nfunction move() {\n  let x = robot.x;\n  let y = robot.y;\n\n  switch (robot.dir) {\n    case 'up':\n      y = Math.min(board.length - 1, y + 1);\n      break;\n    case 'down':\n      y = Math.max(0, y - 1);\n      break;\n    case 'left':\n      x = Math.max(0, x - 1);\n      break;\n    case 'right':\n      x = Math.min(board[y].length - 1, x + 1);\n      break;\n  }\n  // ...\n}\n```\n\nThe newly computed `x` and `y` values represent the desired new position of the robot, but we can only move there if there is no obstacle (e.g., tree, water) in that position. If there is an obstacle in the newly computed position the robot must stay where it is. Otherwise we can update the robot state to reflect the new position.\n\nThe flag is not considered an obstacle but when it is reached we set the `flagReached` state to `true`. And finally, we re-render the board on the screen. The code snippet below shows the complete implementation of the `move()` function.\n\n```js\nfunction move() {\n  let x = robot.x;\n  let y = robot.y;\n\n  switch (robot.dir) {\n    case 'up':\n      y = Math.min(board.length - 1, y + 1);\n      break;\n    case 'down':\n      y = Math.max(0, y - 1);\n      break;\n    case 'left':\n      x = Math.max(0, x - 1);\n      break;\n    case 'right':\n      x = Math.min(board[y].length - 1, x + 1);\n      break;\n  }\n\n  const cell = board[y][x];\n\n  if (cell === '.' || cell === 'F') {\n    board[robot.y][robot.x] = trailIndicators[robot.dir];\n    robot.x = x;\n    robot.y = y;\n    if (cell === 'F') {\n      flagReached = true;\n    }\n  }\n\n  moves += 1;\n  renderBoard();\n}\n```\n\n## Creating HTML elements through the DOM\n\nIn a Single Page Application it is customary to work with a very simple `index.html` file that contains just a single `\u003cdiv\u003e` inside the `\u003cbody\u003e` where HTML elements, dynamically generated by means of JavaScript, are 'mounted'.\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\n\u003chead\u003e\n  \u003cmeta charset=\"UTF-8\"\u003e\n  \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n  \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\"\u003e\n  \u003clink rel=\"stylesheet\" href=\"style.css\"\u003e\n  \u003ctitle\u003eRobot\u003c/title\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n  \u003cdiv id=\"root\"\u003e\u003c/div\u003e\n  \u003cscript src=\"robot.js\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n\n\u003c/html\u003e\n```\n\nFor the robot game we will render the game board as a grid, using an HTML `\u003ctable\u003e` tag. This will become a child of the `\u003cdiv id=root\u003e`. The snippet below can serve as a model of what HTML we should create in our JavaScript code.\n\n```html\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eT\u003c/td\u003e\n    \u003ctd\u003eT\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003eF\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eT\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003eF\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eR\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003eW\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n```\n\nWe need four `\u003ctr\u003e` elements, one for each row. These `\u003ctr\u003e` elements are children of the `\u003ctable\u003e` element.\n\nFor each row we need four `\u003ctd\u003e` elements to contain the contents of each cell on the board. These `\u003ctd\u003e` elements are children of their respective `\u003ctr\u003e` parents.\n\nThis will produce the following board rendition:\n\n![Sample Board](./assets/sample-board.png)\n\nTo create an element we can use:\n\n```js\nconst elem = document.createElement(tagName);\n```\n\nOnce created, the element must be added to the DOM by appending it to its parent:\n\n```js\nparentElement.appendChild(elem);\n```\n\nFor a more complex web page we will soon find that we are repeating the same type of code over and over again: creating an element, appending it to a parent, and (sometimes) setting its text content and setting attributes. We can reduce a lot of repetition by creating a function (called `createAndAppend` below) that performs all these task for us.\n\n```js\nfunction createAndAppend(name, parent, options = {}) {\n  const elem = document.createElement(name);\n  parent.appendChild(elem);\n  Object.keys(options).forEach(function (key) {\n    const value = options[key];\n    if (key === 'text') {\n      elem.innerText = value;\n    } else {\n      elem.setAttribute(key, value);\n    }\n  });\n  return elem;\n}\n```\n\nFor instance, to create an `\u003cimg\u003e` tag as a child of a `\u003ctd\u003e` tag, while setting the `src`, `width` and `height` attributes we could use `createAndAppend()` as follows:\n\n```js\ncreateAndAppend('img', td, { src: `assets/appl.png`, width: 35, height: 35 })\n```\n\n## Application Folders\n\n### 1-robot\n\nA console-based version of the Introduction level of RoverJS. This version needs to be run with Node.\n\nOptional homework assignment: [MAKEME](robot-0/MAKEME.md)\n\n### 2-robot\n\nA manually coded version of the game board in HTML. This serves as an example of the HTML elements that need to be generated programmatically through JavaScript code.\n\n### 3-robot\n\nRenders the game board from `2-robot` by dynamically generating HTML elements through JavaScript.\n\n### 4-robot\n\nAdds the `move()` and `turn()` functions from the console-based version of `1-robot`. The game board is rendered as HTML element as well as text in the console. Because the HTML is rendered synchronously, the intermediate steps towards the flag are not discernable. However, in the console these intermediate steps can be seen.\n\n### 5-robot\n\nAdds a toolbar with buttons to execute robot commands.\n\n### 6-robot\n\n- Introduces the [Model-View-Controller (MVC) pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)  and ES6 classes.\n\n- Adds images to render the robot, trees, water and flag. Uses CSS classes to rotate the robot.\n\n### 7-robot\n\nIntroduces the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) to decouple the View from the Model.\n\n### 8-robot\n\nRetrieves game levels through an XMLHttpRequest. This version of the application must be run from an HTTP server.\nIntroduces separate files for Model and View.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhackyourfuture%2Frobotapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhackyourfuture%2Frobotapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhackyourfuture%2Frobotapp/lists"}