├── .keep ├── .gitignore ├── CODEOWNERS ├── bin ├── server-linux-386 ├── server-linux-amd64 ├── server-windows-386 ├── server-darwin-amd64 └── server-windows-amd64 ├── package.json ├── src ├── client │ ├── assets │ │ ├── stylesheets │ │ │ ├── reset.css │ │ │ ├── home.css │ │ │ ├── results.css │ │ │ ├── create.css │ │ │ ├── form.css │ │ │ ├── progress.css │ │ │ └── index.css │ │ └── javascript │ │ │ └── index.js │ └── pages │ │ ├── results.html │ │ ├── home.html │ │ ├── progress.html │ │ └── race.html └── server │ └── index.js ├── .github └── workflows │ └── manual.yml ├── LICENSE ├── README.md └── data.json /.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @udacity/active-public-content -------------------------------------------------------------------------------- /bin/server-linux-386: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/nd032-c3-asynchronous-programming-with-javascript-project-starter/HEAD/bin/server-linux-386 -------------------------------------------------------------------------------- /bin/server-linux-amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/nd032-c3-asynchronous-programming-with-javascript-project-starter/HEAD/bin/server-linux-amd64 -------------------------------------------------------------------------------- /bin/server-windows-386: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/nd032-c3-asynchronous-programming-with-javascript-project-starter/HEAD/bin/server-windows-386 -------------------------------------------------------------------------------- /bin/server-darwin-amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/nd032-c3-asynchronous-programming-with-javascript-project-starter/HEAD/bin/server-darwin-amd64 -------------------------------------------------------------------------------- /bin/server-windows-amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/nd032-c3-asynchronous-programming-with-javascript-project-starter/HEAD/bin/server-windows-amd64 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pod_racer_sim", 3 | "version": "1.0.0", 4 | "description": "Racing simulator for Pod Racers", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "node src/server/index.js" 9 | }, 10 | "dependencies": { 11 | "body-parser": "^1.19.0", 12 | "express": "^4.17.1", 13 | "node-fetch": "^2.6.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/client/assets/stylesheets/reset.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-size: 16px; 4 | } 5 | 6 | *, *:before, *:after { 7 | box-sizing: border-box; 8 | } 9 | 10 | body, h1, h2, h3, h4, h5, h6, p, ol, ul, li { 11 | margin: 0; 12 | padding: 0; 13 | font-weight: normal; 14 | } 15 | 16 | ol, ul { 17 | list-style: none; 18 | } 19 | 20 | img { 21 | max-width: 100%; 22 | height: auto; 23 | } -------------------------------------------------------------------------------- /src/client/assets/stylesheets/home.css: -------------------------------------------------------------------------------- 1 | /* HOME PAGE SPECIFIC STYLES */ 2 | 3 | /* ------------------- MOBILE --------------------- */ 4 | 5 | 6 | /* ------------------- TABLET --------------------- */ 7 | @media (min-width: 768px) { 8 | 9 | } 10 | 11 | /* ------------------- DESKTOP -------------------- */ 12 | 13 | @media (min-width: 1224px) { 14 | } 15 | 16 | /* ------------------- LARGE SCREENS -------------- */ 17 | 18 | @media (min-width: 1824px) { 19 | } -------------------------------------------------------------------------------- /src/client/assets/stylesheets/results.css: -------------------------------------------------------------------------------- 1 | /* RESULTS PAGE SPECIFIC STYLES */ 2 | 3 | /* ------------------- MOBILE --------------------- */ 4 | 5 | 6 | /* ------------------- TABLET --------------------- */ 7 | @media (min-width: 768px) { 8 | 9 | } 10 | 11 | /* ------------------- DESKTOP -------------------- */ 12 | 13 | @media (min-width: 1224px) { 14 | } 15 | 16 | /* ------------------- LARGE SCREENS -------------- */ 17 | 18 | @media (min-width: 1824px) { 19 | } -------------------------------------------------------------------------------- /src/client/assets/stylesheets/create.css: -------------------------------------------------------------------------------- 1 | /* CREATE PAGE SPECIFIC STYLES */ 2 | 3 | /* ------------------- MOBILE --------------------- */ 4 | 5 | header { 6 | background-image: url('https://via.placeholder.com/1150'); 7 | } 8 | 9 | 10 | /* ------------------- TABLET --------------------- */ 11 | @media (min-width: 768px) { 12 | 13 | } 14 | 15 | /* ------------------- DESKTOP -------------------- */ 16 | 17 | @media (min-width: 1224px) { 18 | } 19 | 20 | /* ------------------- LARGE SCREENS -------------- */ 21 | 22 | @media (min-width: 1824px) { 23 | } -------------------------------------------------------------------------------- /src/client/assets/stylesheets/form.css: -------------------------------------------------------------------------------- 1 | form ul { 2 | display: flex; 3 | padding: 20px; 4 | } 5 | 6 | form li { 7 | border-radius: 6px; 8 | -webkit-box-shadow: 5px 7px 5px 0px rgba(237,230,237,1); 9 | -moz-box-shadow: 5px 7px 5px 0px rgba(237,230,237,1); 10 | box-shadow: 5px 7px 5px 0px rgba(237,230,237,1); 11 | max-width: 300px; 12 | padding: 20px; 13 | border: 1px solid #fbf5f5; 14 | cursor: pointer; 15 | transition: all .6s; 16 | } 17 | 18 | form li:hover { 19 | box-shadow: none; 20 | border: 1px solid rgb(109, 233, 23); 21 | } 22 | 23 | form li.selected { 24 | border: 1px solid rgb(109, 233, 23); 25 | } 26 | 27 | form li h3 { 28 | border-bottom: 1px solid #545454; 29 | padding-bottom: 10px; 30 | margin-bottom: 10px; 31 | } 32 | -------------------------------------------------------------------------------- /src/client/pages/results.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | View Your Race Progress 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

Race Results

17 |
18 |
19 |
20 |

Race Progress

21 |
22 | 23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /src/server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const fetch = require('node-fetch') 4 | const path = require('path') 5 | 6 | const app = express() 7 | const port = 3000 8 | 9 | // setup the ability to see into response bodies 10 | app.use(bodyParser.urlencoded({ extended: false })) 11 | app.use(bodyParser.json()) 12 | 13 | 14 | // setup the express assets path 15 | app.use('/', express.static(path.join(__dirname, '../client'))) 16 | 17 | // API calls ------------------------------------------------------------------------------------ 18 | app.get('/', async (req, res) => { 19 | res.sendFile(path.join(__dirname, '../client/pages/home.html')); 20 | }) 21 | 22 | app.get('/race', async (req, res) => { 23 | res.sendFile(path.join(__dirname, '../client/pages/race.html')); 24 | }) 25 | 26 | app.listen(port, () => console.log(`Example app listening on port ${port}!`)) 27 | -------------------------------------------------------------------------------- /src/client/pages/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to the Pod Racer Simulator 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Welcome to Pod Racer Sim

16 |
17 |
18 |
19 |

Become a Pod Racer Champion

20 |

Start your sim now with just two simple questions

21 | Start My Race 22 |
23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/client/assets/stylesheets/progress.css: -------------------------------------------------------------------------------- 1 | /* PROGRESS PAGE SPECIFIC STYLES */ 2 | 3 | /* ------------------- MOBILE --------------------- */ 4 | header { 5 | background-image: url('https://via.placeholder.com/1150'); 6 | } 7 | 8 | main#two-columns { 9 | margin: 50px 0; 10 | display: flex; 11 | } 12 | 13 | main#two-columns section { 14 | max-width: 50vw; 15 | } 16 | 17 | section#accelerate button { 18 | height: 300px; 19 | width: 300px; 20 | margin: 30px auto; 21 | border-radius: 180%; 22 | background-color: green; 23 | border-color: rgb(14, 166, 6); 24 | cursor: pointer; 25 | } 26 | 27 | #big-numbers { 28 | font-weight: bolder; 29 | font-size: 70px; 30 | } 31 | 32 | /* ------------------- TABLET --------------------- */ 33 | @media (min-width: 768px) { 34 | 35 | } 36 | 37 | /* ------------------- DESKTOP -------------------- */ 38 | 39 | @media (min-width: 1224px) { 40 | } 41 | 42 | /* ------------------- LARGE SCREENS -------------- */ 43 | 44 | @media (min-width: 1824px) { 45 | } 46 | -------------------------------------------------------------------------------- /src/client/pages/progress.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | View Your Race Progress 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |

Race in Progress

18 |
19 |
20 |
21 |

Race Progress

22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
Name
Name
Name
Name
Name
39 |
40 | 41 |
42 | 43 |
44 |
45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/manual.yml: -------------------------------------------------------------------------------- 1 | # Workflow to ensure whenever a Github PR is submitted, 2 | # a JIRA ticket gets created automatically. 3 | name: Manual Workflow 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on pull request events but only for the master branch 8 | pull_request_target: 9 | types: [opened, reopened] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | test-transition-issue: 16 | name: Convert Github Issue to Jira Issue 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@master 21 | 22 | - name: Login 23 | uses: atlassian/gajira-login@master 24 | env: 25 | JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} 26 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} 27 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} 28 | 29 | - name: Create NEW JIRA ticket 30 | id: create 31 | uses: atlassian/gajira-create@master 32 | with: 33 | project: CONUPDATE 34 | issuetype: Task 35 | summary: | 36 | Github PR nd032 - Intermediate JavaScript C3 | Repo: ${{ github.repository }} | PR# ${{github.event.number}} 37 | description: | 38 | Repo link: https://github.com/${{ github.repository }} 39 | PR no. ${{ github.event.pull_request.number }} 40 | PR title: ${{ github.event.pull_request.title }} 41 | PR description: ${{ github.event.pull_request.description }} 42 | In addition, please resolve other issues, if any. 43 | fields: '{"components": [{"name":"nd032 - Intermediate JavaScript"}], "customfield_16449":"https://classroom.udacity.com/", "customfield_16450":"Resolve the PR", "labels": ["github"], "priority":{"id": "4"}}' 44 | 45 | - name: Log created issue 46 | run: echo "Issue ${{ steps.create.outputs.issue }} was created" 47 | -------------------------------------------------------------------------------- /src/client/pages/race.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Start Your Pod Racer Simulation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Start Your Pod Racer Sim

21 |
22 |
23 |
24 |

Get Started

25 |

Create Your Race

26 |
27 |

Choose your track

28 |
    29 | 30 |
31 | 32 | 35 | 36 |

Choose your racer

37 |
    38 | 39 |
40 | 41 | 44 | 45 |
46 |
47 |
48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/client/assets/stylesheets/index.css: -------------------------------------------------------------------------------- 1 | /* ------------------- GENERAL SITE STYLES --------------------- */ 2 | 3 | /* ------------------- MOBILE --------------------- */ 4 | 5 | .sr-only { 6 | position: absolute; 7 | width: 1px; 8 | height: 1px; 9 | padding: 0; 10 | margin: -1px; 11 | overflow: hidden; 12 | clip: rect(0,0,0,0); 13 | border: 0; 14 | } 15 | 16 | body { 17 | display: flex; 18 | flex-direction: column; 19 | font-family: Arial; 20 | min-height: 100vh; 21 | } 22 | 23 | header { 24 | background-image: url('https://via.placeholder.com/1150'); 25 | background-size: cover; 26 | background-repeat: no-repeat; 27 | background-position: center; 28 | height: 100vh; 29 | display: flex; 30 | justify-content: center; 31 | align-items: center; 32 | } 33 | 34 | h1 { 35 | color: white; 36 | text-shadow: 3px 2px hsl(0, 2%, 51%); 37 | font-size: 70px; 38 | text-transform: uppercase; 39 | } 40 | 41 | h2 { 42 | color: black; 43 | font-weight: bold; 44 | font-size: 30px; 45 | line-height: 1.3; 46 | padding: 10px 0; 47 | } 48 | 49 | h3 { 50 | color: black; 51 | font-weight: normal; 52 | font-size: 25px; 53 | } 54 | 55 | h4 { 56 | color: #545454; 57 | font-weight: light; 58 | font-size: 20px; 59 | } 60 | 61 | p { 62 | color: black; 63 | font-weight: normal; 64 | font-size: 18px; 65 | } 66 | 67 | .button { 68 | width: 250px; 69 | padding: 20px 10px; 70 | background-color: green; 71 | cursor: pointer; 72 | border-radius: 4px; 73 | border-color: 1px solid green; 74 | color: white; 75 | font-size: 20px; 76 | text-transform: uppercase; 77 | margin: 20px 0; 78 | display: block; 79 | text-align: center; 80 | text-decoration: none; 81 | } 82 | 83 | main { 84 | flex: 1; 85 | } 86 | 87 | section { 88 | max-width: 1280px; 89 | margin: 0 auto; 90 | padding: 30px 0; 91 | } 92 | 93 | footer { 94 | height: 200px; 95 | background-color: black; 96 | } 97 | 98 | /* ------------------- TABLET --------------------- */ 99 | @media (min-width: 768px) { 100 | header { 101 | height: 100vh; 102 | } 103 | } 104 | 105 | /* ------------------- DESKTOP -------------------- */ 106 | 107 | @media (min-width: 1224px) { 108 | header { 109 | height: 700px; 110 | } 111 | } 112 | 113 | /* ------------------- LARGE SCREENS -------------- */ 114 | 115 | @media (min-width: 1824px) { 116 | header { 117 | height: 700px; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright © 2012 - 2020, Udacity, Inc. 3 | 4 | Udacity hereby grants you a license in and to the Educational Content, including but not limited to homework assignments, programming assignments, code samples, and other educational materials and tools (as further described in the Udacity Terms of Use), subject to, as modified herein, the terms and conditions of the Creative Commons Attribution-NonCommercial- NoDerivs 3.0 License located at http://creativecommons.org/licenses/by-nc-nd/4.0 and successor locations for such license (the "CC License") provided that, in each case, the Educational Content is specifically marked as being subject to the CC License. 5 | Udacity expressly defines the following as falling outside the definition of "non-commercial": 6 | (a) the sale or rental of (i) any part of the Educational Content, (ii) any derivative works based at least in part on the Educational Content, or (iii) any collective work that includes any part of the Educational Content; 7 | (b) the sale of access or a link to any part of the Educational Content without first obtaining informed consent from the buyer (that the buyer is aware that the Educational Content, or such part thereof, is available at the Website free of charge); 8 | (c) providing training, support, or editorial services that use or reference the Educational Content in exchange for a fee; 9 | (d) the sale of advertisements, sponsorships, or promotions placed on the Educational Content, or any part thereof, or the sale of advertisements, sponsorships, or promotions on any website or blog containing any part of the Educational Material, including without limitation any "pop-up advertisements"; 10 | (e) the use of Educational Content by a college, university, school, or other educational institution for instruction where tuition is charged; and 11 | (f) the use of Educational Content by a for-profit corporation or non-profit entity for internal professional development or training. 12 | 13 | 14 | 15 | THE SERVICES AND ONLINE COURSES (INCLUDING ANY CONTENT) ARE PROVIDED "AS IS" AND "AS AVAILABLE" WITH NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. YOU ASSUME TOTAL RESPONSIBILITY AND THE ENTIRE RISK FOR YOUR USE OF THE SERVICES, ONLINE COURSES, AND CONTENT. WITHOUT LIMITING THE FOREGOING, WE DO NOT WARRANT THAT (A) THE SERVICES, WEBSITES, CONTENT, OR THE ONLINE COURSES WILL MEET YOUR REQUIREMENTS OR EXPECTATIONS OR ACHIEVE THE INTENDED PURPOSES, (B) THE WEBSITES OR THE ONLINE COURSES WILL NOT EXPERIENCE OUTAGES OR OTHERWISE BE UNINTERRUPTED, TIMELY, SECURE OR ERROR-FREE, (C) THE INFORMATION OR CONTENT OBTAINED THROUGH THE SERVICES, SUCH AS CHAT ROOM SERVICES, WILL BE ACCURATE, COMPLETE, CURRENT, ERROR- FREE, COMPLETELY SECURE OR RELIABLE, OR (D) THAT DEFECTS IN OR ON THE SERVICES OR CONTENT WILL BE CORRECTED. YOU ASSUME ALL RISK OF PERSONAL INJURY, INCLUDING DEATH AND DAMAGE TO PERSONAL PROPERTY, SUSTAINED FROM USE OF SERVICES. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to the One and only UdaciRacer Simulation Game 2 | 3 | ## Project Introduction 4 | 5 | Here is a partially built-out game that races cars—your job is to complete it! Throughout the game logic, you will find _"TODO"_ comments that must be completed in order for the game to work. You are going to use the asynchronous skills you gained in the course to fill in the blanks in this game. 6 | 7 | The game mechanics are this: you select a player and track, the game begins and you accelerate your racer by clicking an acceleration button. As you accelerate so do the other players and the leaderboard live-updates as players change position on the track. The final view is a results page displaying the players' rankings. 8 | 9 | The game has three main views: 10 | 11 | 1. The form to create a race 12 | 13 | 2. The race progress view (this includes the live-updating leaderboard and acceleration button) 14 | 15 | 3. The race results view 16 | 17 | ## Starter Code 18 | 19 | We have supplied you with the following: 20 | 21 | 1. An API. The API is provided in the form of a binary held in the bin folder. You never need to open the binary file, as there are no edits you can make to it. Your work will be 100% in the front end. 22 | 23 | 2. HTML Views. The focus of this course is not UI development or styling practice, so we have already provided you with pieces of UI, all you have to do is call them at the right times. 24 | 25 | ## Getting Started 26 | 27 | In order to build this game, we need to run two things: the game engine API and the front end. 28 | 29 | ### Start the Server 30 | 31 | The game engine has been compiled down to a binary so that you can run it on any system. Because of this, you cannot edit the API in any way, it is just a black box that we interact with via the API endpoints. 32 | 33 | To run the server, locate your operating system and run the associated command in your terminal at the root of the project. 34 | 35 | | Your OS | Command to start the API | 36 | | --------------------- | --------------------------------------------------------- | 37 | | Mac | `ORIGIN_ALLOWED=http://localhost:3000 ./bin/server-darwin-amd64` | 38 | | Windows | `ORIGIN_ALLOWED=http://localhost:3000 ./bin/server-windows-amd64.exe` | 39 | | Linux (Ubuntu, etc..) | `ORIGIN_ALLOWED=http://localhost:3000 ./bin/server-linux-amd64` | 40 | 41 | Note that this process will use your terminal tab, so you will have to open a new tab and navigate back to the project root to start the front end. 42 | 43 | #### WINDOWS USERS -- Setting Environment Variables 44 | If you are using a windows machine: 45 | 1. `cd` into the root of the project containing data.json 46 | 2. Run the following command to add the environment variable: 47 | ```set DATA_FILE=./data.json``` 48 | 49 | If you still run into issues running the API server on your machine, you can run this project in the Udacity classroom. 50 | 51 | 52 | ### Start the Frontend 53 | 54 | First, run your preference of `npm install && npm start` or `yarn && yarn start` at the root of this project. Then you should be able to access http://localhost:3000. 55 | 56 | ## Project Requirements 57 | 58 | This starter code base has directions for you in `src/client/assets/javascript/index.js`. There you will be directed to use certain asynchronous methods to achieve tasks. You will know you're making progress as you can play through more and more of the game. 59 | 60 | ### API Calls 61 | 62 | To complete the project you must first create the calls to the API. These will all be fetch requests, and all information needed to create the request is provided in the instructions. The API calls are all at the bottom of the file: `src/client/assets/javascript/index.js`. 63 | 64 | Below are a list of the API endpoints and the shape of the data they return. These are all of the endpoints you need to complete the game. Consult this information often as you complete the project: 65 | 66 | [GET] `api/tracks` 67 | List of all tracks 68 | 69 | - id: number (1) 70 | - name: string ("Track 1") 71 | - segments: number[]([87,47,29,31,78,25,80,76,60,14....]) 72 | 73 | [GET] `api/cars` 74 | List of all cars 75 | 76 | - id: number (3) 77 | - driver_name: string ("Racer 1") 78 | - top_speed: number (500) 79 | - acceleration: number (10) 80 | - handling: number (10) 81 | 82 | [GET] `api/races/${id}` 83 | Information about a single race 84 | 85 | - status: RaceStatus ("unstarted" | "in-progress" | "finished") 86 | - positions object[] ([{ car: object, final_position: number (omitted if empty), speed: number, segment: number}]) 87 | 88 | [POST] `api/races` 89 | Create a race 90 | 91 | - id: number 92 | - track: string 93 | - player_id: number 94 | - cars: Cars[] (array of cars in the race) 95 | - results: Cars[] (array of cars in the position they finished, available if the race is finished) 96 | 97 | [POST] `api/races/${id}/start` 98 | Begin a race 99 | 100 | - Returns nothing 101 | 102 | [POST] `api/races/${id}/accelerate` 103 | Accelerate a car 104 | 105 | - Returns nothing 106 | 107 | To complete the race logic, find all the TODO tags in index.js and read the instructions. 108 | -------------------------------------------------------------------------------- /src/client/assets/javascript/index.js: -------------------------------------------------------------------------------- 1 | // PROVIDED CODE BELOW (LINES 1 - 80) DO NOT REMOVE 2 | 3 | // The store will hold all information needed globally 4 | let store = { 5 | track_id: undefined, 6 | player_id: undefined, 7 | race_id: undefined, 8 | } 9 | 10 | // We need our javascript to wait until the DOM is loaded 11 | document.addEventListener("DOMContentLoaded", function() { 12 | onPageLoad() 13 | setupClickHandlers() 14 | }) 15 | 16 | async function onPageLoad() { 17 | try { 18 | getTracks() 19 | .then(tracks => { 20 | const html = renderTrackCards(tracks) 21 | renderAt('#tracks', html) 22 | }) 23 | 24 | getRacers() 25 | .then((racers) => { 26 | const html = renderRacerCars(racers) 27 | renderAt('#racers', html) 28 | }) 29 | } catch(error) { 30 | console.log("Problem getting tracks and racers ::", error.message) 31 | console.error(error) 32 | } 33 | } 34 | 35 | function setupClickHandlers() { 36 | document.addEventListener('click', function(event) { 37 | const { target } = event 38 | 39 | // Race track form field 40 | if (target.matches('.card.track')) { 41 | handleSelectTrack(target) 42 | } 43 | 44 | // Podracer form field 45 | if (target.matches('.card.podracer')) { 46 | handleSelectPodRacer(target) 47 | } 48 | 49 | // Submit create race form 50 | if (target.matches('#submit-create-race')) { 51 | event.preventDefault() 52 | 53 | // start race 54 | handleCreateRace() 55 | } 56 | 57 | // Handle acceleration click 58 | if (target.matches('#gas-peddle')) { 59 | handleAccelerate() 60 | } 61 | 62 | }, false) 63 | } 64 | 65 | async function delay(ms) { 66 | try { 67 | return await new Promise(resolve => setTimeout(resolve, ms)); 68 | } catch(error) { 69 | console.log("an error shouldn't be possible here") 70 | console.log(error) 71 | } 72 | } 73 | // ^ PROVIDED CODE ^ DO NOT REMOVE 74 | 75 | // This async function controls the flow of the race, add the logic and error handling 76 | async function handleCreateRace() { 77 | // render starting UI 78 | renderAt('#race', renderRaceStartView()) 79 | 80 | // TODO - Get player_id and track_id from the store 81 | 82 | // const race = TODO - invoke the API call to create the race, then save the result 83 | 84 | // TODO - update the store with the race id 85 | // For the API to work properly, the race id should be race id - 1 86 | 87 | // The race has been created, now start the countdown 88 | // TODO - call the async function runCountdown 89 | 90 | // TODO - call the async function startRace 91 | 92 | // TODO - call the async function runRace 93 | } 94 | 95 | function runRace(raceID) { 96 | return new Promise(resolve => { 97 | // TODO - use Javascript's built in setInterval method to get race info every 500ms 98 | 99 | /* 100 | TODO - if the race info status property is "in-progress", update the leaderboard by calling: 101 | 102 | renderAt('#leaderBoard', raceProgress(res.positions)) 103 | */ 104 | 105 | /* 106 | TODO - if the race info status property is "finished", run the following: 107 | 108 | clearInterval(raceInterval) // to stop the interval from repeating 109 | renderAt('#race', resultsView(res.positions)) // to render the results view 110 | reslove(res) // resolve the promise 111 | */ 112 | }) 113 | // remember to add error handling for the Promise 114 | } 115 | 116 | async function runCountdown() { 117 | try { 118 | // wait for the DOM to load 119 | await delay(1000) 120 | let timer = 3 121 | 122 | return new Promise(resolve => { 123 | // TODO - use Javascript's built in setInterval method to count down once per second 124 | 125 | // run this DOM manipulation to decrement the countdown for the user 126 | document.getElementById('big-numbers').innerHTML = --timer 127 | 128 | // TODO - if the countdown is done, clear the interval, resolve the promise, and return 129 | 130 | }) 131 | } catch(error) { 132 | console.log(error); 133 | } 134 | } 135 | 136 | function handleSelectPodRacer(target) { 137 | console.log("selected a pod", target.id) 138 | 139 | // remove class selected from all racer options 140 | const selected = document.querySelector('#racers .selected') 141 | if(selected) { 142 | selected.classList.remove('selected') 143 | } 144 | 145 | // add class selected to current target 146 | target.classList.add('selected') 147 | 148 | // TODO - save the selected racer to the store 149 | } 150 | 151 | function handleSelectTrack(target) { 152 | console.log("selected a track", target.id) 153 | 154 | // remove class selected from all track options 155 | const selected = document.querySelector('#tracks .selected') 156 | if(selected) { 157 | selected.classList.remove('selected') 158 | } 159 | 160 | // add class selected to current target 161 | target.classList.add('selected') 162 | 163 | // TODO - save the selected track id to the store 164 | 165 | } 166 | 167 | function handleAccelerate() { 168 | console.log("accelerate button clicked") 169 | // TODO - Invoke the API call to accelerate 170 | } 171 | 172 | // HTML VIEWS ------------------------------------------------ 173 | // Provided code - do not remove 174 | 175 | function renderRacerCars(racers) { 176 | if (!racers.length) { 177 | return ` 178 |

Loading Racers... 179 | ` 180 | } 181 | 182 | const results = racers.map(renderRacerCard).join('') 183 | 184 | return ` 185 | 188 | ` 189 | } 190 | 191 | function renderRacerCard(racer) { 192 | const { id, driver_name, top_speed, acceleration, handling } = racer 193 | 194 | return ` 195 |
  • 196 |

    ${driver_name}

    197 |

    ${top_speed}

    198 |

    ${acceleration}

    199 |

    ${handling}

    200 |
  • 201 | ` 202 | } 203 | 204 | function renderTrackCards(tracks) { 205 | if (!tracks.length) { 206 | return ` 207 |

    Loading Tracks... 208 | ` 209 | } 210 | 211 | const results = tracks.map(renderTrackCard).join('') 212 | 213 | return ` 214 | 217 | ` 218 | } 219 | 220 | function renderTrackCard(track) { 221 | const { id, name } = track 222 | 223 | return ` 224 |
  • 225 |

    ${name}

    226 |
  • 227 | ` 228 | } 229 | 230 | function renderCountdown(count) { 231 | return ` 232 |

    Race Starts In...

    233 |

    ${count}

    234 | ` 235 | } 236 | 237 | function renderRaceStartView(track, racers) { 238 | return ` 239 |
    240 |

    Race: ${track.name}

    241 |
    242 |
    243 |
    244 | ${renderCountdown(3)} 245 |
    246 | 247 |
    248 |

    Directions

    249 |

    Click the button as fast as you can to make your racer go faster!

    250 | 251 |
    252 |
    253 | 254 | ` 255 | } 256 | 257 | function resultsView(positions) { 258 | positions.sort((a, b) => (a.final_position > b.final_position) ? 1 : -1) 259 | 260 | return ` 261 |
    262 |

    Race Results

    263 |
    264 |
    265 | ${raceProgress(positions)} 266 | Start a new race 267 |
    268 | ` 269 | } 270 | 271 | function raceProgress(positions) { 272 | let userPlayer = positions.find(e => e.id === store.player_id) 273 | userPlayer.driver_name += " (you)" 274 | 275 | positions = positions.sort((a, b) => (a.segment > b.segment) ? -1 : 1) 276 | let count = 1 277 | 278 | const results = positions.map(p => { 279 | return ` 280 | 281 | 282 |

    ${count++} - ${p.driver_name}

    283 | 284 | 285 | ` 286 | }) 287 | 288 | return ` 289 |
    290 |

    Leaderboard

    291 |
    292 | ${results} 293 |
    294 |
    295 | ` 296 | } 297 | 298 | function renderAt(element, html) { 299 | const node = document.querySelector(element) 300 | 301 | node.innerHTML = html 302 | } 303 | 304 | // ^ Provided code ^ do not remove 305 | 306 | 307 | // API CALLS ------------------------------------------------ 308 | 309 | const SERVER = 'http://localhost:3001' 310 | 311 | function defaultFetchOpts() { 312 | return { 313 | mode: 'cors', 314 | headers: { 315 | 'Content-Type': 'application/json', 316 | 'Access-Control-Allow-Origin' : SERVER, 317 | }, 318 | } 319 | } 320 | 321 | // TODO - Make a fetch call (with error handling!) to each of the following API endpoints 322 | 323 | function getTracks() { 324 | // GET request to `${SERVER}/api/tracks` 325 | } 326 | 327 | function getRacers() { 328 | // GET request to `${SERVER}/api/cars` 329 | } 330 | 331 | function createRace(player_id, track_id) { 332 | player_id = parseInt(player_id) 333 | track_id = parseInt(track_id) 334 | const body = { player_id, track_id } 335 | 336 | return fetch(`${SERVER}/api/races`, { 337 | method: 'POST', 338 | ...defaultFetchOpts(), 339 | dataType: 'jsonp', 340 | body: JSON.stringify(body) 341 | }) 342 | .then(res => res.json()) 343 | .catch(err => console.log("Problem with createRace request::", err)) 344 | } 345 | 346 | function getRace(id) { 347 | // GET request to `${SERVER}/api/races/${id}` 348 | } 349 | 350 | function startRace(id) { 351 | return fetch(`${SERVER}/api/races/${id}/start`, { 352 | method: 'POST', 353 | ...defaultFetchOpts(), 354 | }) 355 | .then(res => res.json()) 356 | .catch(err => console.log("Problem with getRace request::", err)) 357 | } 358 | 359 | function accelerate(id) { 360 | // POST request to `${SERVER}/api/races/${id}/accelerate` 361 | // options parameter provided as defaultFetchOpts 362 | // no body or datatype needed for this request 363 | } 364 | -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | { 2 | "cars": [ 3 | { 4 | "id": 1, 5 | "driver_name": "Racer 1", 6 | "top_speed": 650, 7 | "acceleration": 8, 8 | "handling": 7 9 | }, 10 | { 11 | "id": 2, 12 | "driver_name": "Racer 2", 13 | "top_speed": 600, 14 | "acceleration": 9, 15 | "handling": 9 16 | }, 17 | { 18 | "id": 3, 19 | "driver_name": "Racer 3", 20 | "top_speed": 500, 21 | "acceleration": 10, 22 | "handling": 6 23 | }, 24 | { 25 | "id": 4, 26 | "driver_name": "Racer 4", 27 | "top_speed": 550, 28 | "acceleration": 7, 29 | "handling": 8 30 | }, 31 | { 32 | "id": 5, 33 | "driver_name": "Racer 5", 34 | "top_speed": 700, 35 | "acceleration": 5, 36 | "handling": 4 37 | } 38 | ], 39 | "tracks": [ 40 | { 41 | "id": 1, 42 | "name": "Track 1", 43 | "segments": [ 44 | 87, 45 | 47, 46 | 29, 47 | 31, 48 | 78, 49 | 25, 50 | 80, 51 | 76, 52 | 60, 53 | 14, 54 | 61, 55 | 12, 56 | 29, 57 | 58, 58 | 44, 59 | 81, 60 | 65, 61 | 47, 62 | 86, 63 | 65, 64 | 36, 65 | 68, 66 | 28, 67 | 27, 68 | 7, 69 | 7, 70 | 28, 71 | 60, 72 | 75, 73 | 31, 74 | 78, 75 | 77, 76 | 41, 77 | 39, 78 | 26, 79 | 87, 80 | 61, 81 | 75, 82 | 36, 83 | 43, 84 | 40, 85 | 64, 86 | 63, 87 | 73, 88 | 27, 89 | 88, 90 | 64, 91 | 19, 92 | 3, 93 | 27, 94 | 61, 95 | 9, 96 | 69, 97 | 40, 98 | 25, 99 | 8, 100 | 48, 101 | 43, 102 | 5, 103 | 71, 104 | 60, 105 | 75, 106 | 66, 107 | 46, 108 | 8, 109 | 71, 110 | 62, 111 | 23, 112 | 56, 113 | 73, 114 | 36, 115 | 62, 116 | 68, 117 | 87, 118 | 54, 119 | 37, 120 | 13, 121 | 16, 122 | 80, 123 | 33, 124 | 63, 125 | 7, 126 | 73, 127 | 11, 128 | 79, 129 | 83, 130 | 23, 131 | 71, 132 | 82, 133 | 38, 134 | 76, 135 | 6, 136 | 37, 137 | 0, 138 | 13, 139 | 12, 140 | 63, 141 | 25, 142 | 38, 143 | 75, 144 | 31, 145 | 75, 146 | 17, 147 | 77, 148 | 20, 149 | 10, 150 | 85, 151 | 30, 152 | 82, 153 | 78, 154 | 73, 155 | 31, 156 | 62, 157 | 54, 158 | 67, 159 | 77, 160 | 67, 161 | 21, 162 | 54, 163 | 56, 164 | 82, 165 | 81, 166 | 39, 167 | 76, 168 | 60, 169 | 23, 170 | 76, 171 | 49, 172 | 81, 173 | 72, 174 | 15, 175 | 25, 176 | 80, 177 | 27, 178 | 79, 179 | 8, 180 | 38, 181 | 54, 182 | 33, 183 | 24, 184 | 17, 185 | 22, 186 | 52, 187 | 26, 188 | 59, 189 | 20, 190 | 56, 191 | 71, 192 | 56, 193 | 40, 194 | 61, 195 | 34, 196 | 64, 197 | 65, 198 | 13, 199 | 21, 200 | 30, 201 | 52, 202 | 38, 203 | 67, 204 | 81, 205 | 58, 206 | 24, 207 | 42, 208 | 63, 209 | 42, 210 | 78, 211 | 33, 212 | 88, 213 | 66, 214 | 0, 215 | 35, 216 | 30, 217 | 44, 218 | 22, 219 | 67, 220 | 45, 221 | 21, 222 | 59, 223 | 10, 224 | 63, 225 | 70, 226 | 39, 227 | 50, 228 | 63, 229 | 30, 230 | 84, 231 | 67, 232 | 70, 233 | 5, 234 | 62, 235 | 59, 236 | 60, 237 | 78, 238 | 46, 239 | 35, 240 | 6, 241 | 50, 242 | 56, 243 | 9, 244 | 12 245 | ] 246 | }, 247 | { 248 | "id": 2, 249 | "name": "Track 2", 250 | "segments": [ 251 | 37, 252 | 86, 253 | 20, 254 | 59, 255 | 72, 256 | 7, 257 | 42, 258 | 28, 259 | 51, 260 | 83, 261 | 48, 262 | 28, 263 | 66, 264 | 79, 265 | 47, 266 | 67, 267 | 12, 268 | 75, 269 | 1, 270 | 23, 271 | 25, 272 | 77, 273 | 33, 274 | 10, 275 | 16, 276 | 16, 277 | 2, 278 | 10, 279 | 50, 280 | 52, 281 | 69, 282 | 11, 283 | 40, 284 | 70, 285 | 29, 286 | 84, 287 | 24, 288 | 60, 289 | 51, 290 | 51, 291 | 77, 292 | 6, 293 | 80, 294 | 29, 295 | 37, 296 | 73, 297 | 51, 298 | 64, 299 | 85, 300 | 89, 301 | 55, 302 | 29, 303 | 74, 304 | 0, 305 | 62, 306 | 70, 307 | 30, 308 | 74, 309 | 13, 310 | 65, 311 | 33, 312 | 41, 313 | 30, 314 | 44, 315 | 49, 316 | 50, 317 | 76, 318 | 8, 319 | 82, 320 | 34, 321 | 15, 322 | 84, 323 | 57, 324 | 24, 325 | 3, 326 | 6, 327 | 68, 328 | 12, 329 | 70, 330 | 12, 331 | 1, 332 | 47, 333 | 1, 334 | 65, 335 | 22, 336 | 49, 337 | 2, 338 | 79, 339 | 21, 340 | 71, 341 | 15, 342 | 51, 343 | 13, 344 | 53, 345 | 66, 346 | 69, 347 | 47, 348 | 68, 349 | 48, 350 | 28, 351 | 83, 352 | 34, 353 | 4, 354 | 85, 355 | 14, 356 | 16, 357 | 67, 358 | 56, 359 | 27, 360 | 24, 361 | 48, 362 | 9, 363 | 66, 364 | 33, 365 | 24, 366 | 77, 367 | 67, 368 | 34, 369 | 9, 370 | 61, 371 | 86, 372 | 73, 373 | 28, 374 | 6, 375 | 16, 376 | 67, 377 | 42, 378 | 66, 379 | 31, 380 | 75, 381 | 22, 382 | 79, 383 | 88, 384 | 37, 385 | 60, 386 | 19, 387 | 48, 388 | 49, 389 | 51, 390 | 22, 391 | 8, 392 | 75, 393 | 11, 394 | 73, 395 | 50, 396 | 30, 397 | 21, 398 | 15, 399 | 26, 400 | 69, 401 | 36 402 | ] 403 | }, 404 | { 405 | "id": 3, 406 | "name": "Track 3", 407 | "segments": [ 408 | 15, 409 | 10, 410 | 64, 411 | 17, 412 | 47, 413 | 18, 414 | 65, 415 | 3, 416 | 50, 417 | 18, 418 | 21, 419 | 35, 420 | 18, 421 | 4, 422 | 12, 423 | 33, 424 | 12, 425 | 3, 426 | 62, 427 | 13, 428 | 48, 429 | 17, 430 | 46, 431 | 38, 432 | 3, 433 | 11, 434 | 54, 435 | 26, 436 | 60, 437 | 71, 438 | 42, 439 | 57, 440 | 52, 441 | 4, 442 | 43, 443 | 70, 444 | 16, 445 | 17, 446 | 2, 447 | 4, 448 | 31, 449 | 3, 450 | 48, 451 | 46, 452 | 75, 453 | 72, 454 | 45, 455 | 24, 456 | 66, 457 | 69, 458 | 44, 459 | 0, 460 | 34, 461 | 89, 462 | 67, 463 | 64, 464 | 27, 465 | 83, 466 | 14, 467 | 35, 468 | 30, 469 | 59, 470 | 74, 471 | 15, 472 | 9, 473 | 41, 474 | 1, 475 | 1, 476 | 55, 477 | 9, 478 | 43, 479 | 35, 480 | 52, 481 | 22, 482 | 15, 483 | 27, 484 | 42, 485 | 23, 486 | 87, 487 | 79, 488 | 17, 489 | 20, 490 | 65, 491 | 6, 492 | 54, 493 | 24, 494 | 3, 495 | 84, 496 | 12, 497 | 53, 498 | 44, 499 | 40, 500 | 50, 501 | 5, 502 | 44, 503 | 3, 504 | 70, 505 | 29, 506 | 37, 507 | 15, 508 | 61, 509 | 8, 510 | 28, 511 | 84, 512 | 79, 513 | 24, 514 | 41, 515 | 17, 516 | 56, 517 | 60, 518 | 81, 519 | 67, 520 | 31, 521 | 69, 522 | 88, 523 | 80, 524 | 54, 525 | 51, 526 | 70, 527 | 33, 528 | 16, 529 | 76, 530 | 75, 531 | 40, 532 | 72, 533 | 13, 534 | 64, 535 | 29, 536 | 5, 537 | 6, 538 | 81, 539 | 34, 540 | 13, 541 | 86, 542 | 77, 543 | 48, 544 | 83, 545 | 55, 546 | 16, 547 | 19, 548 | 30, 549 | 64, 550 | 70, 551 | 16, 552 | 50, 553 | 81, 554 | 74, 555 | 39, 556 | 26, 557 | 83, 558 | 15, 559 | 1, 560 | 32, 561 | 9, 562 | 74, 563 | 71, 564 | 4, 565 | 81, 566 | 60, 567 | 35, 568 | 88, 569 | 4, 570 | 70, 571 | 6, 572 | 59, 573 | 2, 574 | 36, 575 | 83, 576 | 76, 577 | 62, 578 | 1, 579 | 85, 580 | 17, 581 | 13, 582 | 87, 583 | 88 584 | ] 585 | }, 586 | { 587 | "id": 4, 588 | "name": "Track 4", 589 | "segments": [ 590 | 9, 591 | 55, 592 | 43, 593 | 70, 594 | 27, 595 | 34, 596 | 22, 597 | 48, 598 | 17, 599 | 37, 600 | 9, 601 | 32, 602 | 6, 603 | 39, 604 | 4, 605 | 56, 606 | 4, 607 | 59, 608 | 73, 609 | 42, 610 | 64, 611 | 29, 612 | 30, 613 | 29, 614 | 23, 615 | 20, 616 | 44, 617 | 75, 618 | 41, 619 | 53, 620 | 21, 621 | 38, 622 | 7, 623 | 84, 624 | 52, 625 | 13, 626 | 11, 627 | 30, 628 | 45, 629 | 10, 630 | 22, 631 | 42, 632 | 71, 633 | 8, 634 | 61, 635 | 57, 636 | 12, 637 | 55, 638 | 66, 639 | 72, 640 | 73, 641 | 75, 642 | 19, 643 | 80, 644 | 84, 645 | 8, 646 | 50, 647 | 57, 648 | 2, 649 | 4, 650 | 40, 651 | 72, 652 | 38, 653 | 16, 654 | 26, 655 | 46, 656 | 35, 657 | 38, 658 | 2, 659 | 63, 660 | 31, 661 | 61, 662 | 3, 663 | 69, 664 | 73, 665 | 30, 666 | 47, 667 | 46, 668 | 51, 669 | 50, 670 | 88, 671 | 75, 672 | 41, 673 | 8, 674 | 68, 675 | 52, 676 | 35, 677 | 85, 678 | 41, 679 | 88, 680 | 66, 681 | 7, 682 | 54, 683 | 79, 684 | 33, 685 | 11, 686 | 51, 687 | 40, 688 | 2, 689 | 44, 690 | 54, 691 | 78, 692 | 3, 693 | 25, 694 | 88, 695 | 50, 696 | 10, 697 | 22, 698 | 60, 699 | 6, 700 | 39, 701 | 30, 702 | 74, 703 | 40, 704 | 61, 705 | 56, 706 | 51, 707 | 82, 708 | 20, 709 | 74, 710 | 83, 711 | 62, 712 | 28, 713 | 7, 714 | 63, 715 | 60, 716 | 13, 717 | 28, 718 | 31, 719 | 6, 720 | 68, 721 | 41, 722 | 27, 723 | 22, 724 | 7, 725 | 37, 726 | 54, 727 | 40, 728 | 77, 729 | 2, 730 | 45, 731 | 15, 732 | 18, 733 | 10, 734 | 84, 735 | 52, 736 | 42, 737 | 25, 738 | 64, 739 | 87, 740 | 83, 741 | 11 742 | ] 743 | }, 744 | { 745 | "id": 5, 746 | "name": "Track 5", 747 | "segments": [ 748 | 61, 749 | 5, 750 | 30, 751 | 55, 752 | 67, 753 | 19, 754 | 78, 755 | 89, 756 | 35, 757 | 25, 758 | 73, 759 | 10, 760 | 10, 761 | 29, 762 | 3, 763 | 51, 764 | 22, 765 | 28, 766 | 1, 767 | 16, 768 | 49, 769 | 35, 770 | 65, 771 | 11, 772 | 58, 773 | 44, 774 | 83, 775 | 36, 776 | 35, 777 | 50, 778 | 76, 779 | 56, 780 | 3, 781 | 28, 782 | 71, 783 | 22, 784 | 59, 785 | 73, 786 | 43, 787 | 47, 788 | 12, 789 | 53, 790 | 33, 791 | 15, 792 | 0, 793 | 67, 794 | 71, 795 | 11, 796 | 47, 797 | 28, 798 | 67, 799 | 9, 800 | 4, 801 | 6, 802 | 2, 803 | 74, 804 | 58, 805 | 77, 806 | 50, 807 | 2, 808 | 68, 809 | 65, 810 | 57, 811 | 34, 812 | 8, 813 | 38, 814 | 67, 815 | 3, 816 | 34, 817 | 82, 818 | 11, 819 | 61, 820 | 8, 821 | 52, 822 | 68, 823 | 40, 824 | 61, 825 | 1, 826 | 31, 827 | 24, 828 | 17, 829 | 16, 830 | 48, 831 | 74, 832 | 84, 833 | 57, 834 | 58, 835 | 23, 836 | 34, 837 | 25, 838 | 72, 839 | 25, 840 | 59, 841 | 67, 842 | 88, 843 | 62, 844 | 15, 845 | 6, 846 | 48, 847 | 49, 848 | 12, 849 | 81, 850 | 45, 851 | 62, 852 | 74, 853 | 20, 854 | 19, 855 | 33, 856 | 84, 857 | 30, 858 | 60, 859 | 19, 860 | 67, 861 | 25, 862 | 55, 863 | 78, 864 | 53, 865 | 5, 866 | 33, 867 | 1, 868 | 60, 869 | 22, 870 | 52, 871 | 82, 872 | 68, 873 | 34, 874 | 18, 875 | 69, 876 | 38, 877 | 19, 878 | 16, 879 | 36, 880 | 87, 881 | 52, 882 | 88, 883 | 14, 884 | 33, 885 | 36, 886 | 63, 887 | 51, 888 | 3, 889 | 4, 890 | 15, 891 | 30, 892 | 46, 893 | 45, 894 | 83, 895 | 18, 896 | 51, 897 | 31, 898 | 0, 899 | 2, 900 | 18, 901 | 44, 902 | 66, 903 | 82, 904 | 23, 905 | 71, 906 | 25, 907 | 1, 908 | 69, 909 | 28, 910 | 4, 911 | 21, 912 | 23, 913 | 6, 914 | 71, 915 | 46, 916 | 6, 917 | 19, 918 | 56, 919 | 37, 920 | 84, 921 | 37, 922 | 83, 923 | 26, 924 | 41, 925 | 4, 926 | 3, 927 | 29, 928 | 21, 929 | 13, 930 | 74, 931 | 30, 932 | 2, 933 | 41, 934 | 77, 935 | 30, 936 | 47, 937 | 74, 938 | 43, 939 | 47, 940 | 63 941 | ] 942 | }, 943 | { 944 | "id": 6, 945 | "name": "Track 6", 946 | "segments": [ 947 | 18, 948 | 7, 949 | 4, 950 | 66, 951 | 76, 952 | 15, 953 | 5, 954 | 15, 955 | 15, 956 | 79, 957 | 81, 958 | 53, 959 | 44, 960 | 49, 961 | 17, 962 | 73, 963 | 26, 964 | 10, 965 | 43, 966 | 10, 967 | 67, 968 | 27, 969 | 12, 970 | 54, 971 | 18, 972 | 21, 973 | 85, 974 | 87, 975 | 72, 976 | 73, 977 | 79, 978 | 44, 979 | 44, 980 | 79, 981 | 18, 982 | 72, 983 | 18, 984 | 60, 985 | 40, 986 | 68, 987 | 47, 988 | 15, 989 | 23, 990 | 59, 991 | 15, 992 | 60, 993 | 32, 994 | 1, 995 | 62, 996 | 69, 997 | 86, 998 | 57, 999 | 48, 1000 | 56, 1001 | 38, 1002 | 23, 1003 | 8, 1004 | 83, 1005 | 56, 1006 | 53, 1007 | 44, 1008 | 21, 1009 | 43, 1010 | 38, 1011 | 76, 1012 | 24, 1013 | 64, 1014 | 63, 1015 | 30, 1016 | 61, 1017 | 69, 1018 | 22, 1019 | 27, 1020 | 89, 1021 | 9, 1022 | 9, 1023 | 75, 1024 | 33, 1025 | 18, 1026 | 50, 1027 | 37, 1028 | 67, 1029 | 2, 1030 | 82, 1031 | 81, 1032 | 52, 1033 | 68, 1034 | 82, 1035 | 5, 1036 | 48, 1037 | 65, 1038 | 23, 1039 | 48, 1040 | 25, 1041 | 86, 1042 | 41, 1043 | 85, 1044 | 66, 1045 | 11, 1046 | 24, 1047 | 1, 1048 | 89, 1049 | 24, 1050 | 68, 1051 | 62, 1052 | 40, 1053 | 37, 1054 | 1, 1055 | 24, 1056 | 41, 1057 | 83, 1058 | 64, 1059 | 61, 1060 | 23, 1061 | 42, 1062 | 22, 1063 | 78, 1064 | 86, 1065 | 80, 1066 | 80, 1067 | 32, 1068 | 35, 1069 | 19, 1070 | 83, 1071 | 32, 1072 | 81, 1073 | 57, 1074 | 84, 1075 | 62, 1076 | 22, 1077 | 34, 1078 | 33, 1079 | 37, 1080 | 9, 1081 | 75, 1082 | 54, 1083 | 39, 1084 | 79, 1085 | 67, 1086 | 6, 1087 | 20, 1088 | 72, 1089 | 32, 1090 | 0, 1091 | 8, 1092 | 13, 1093 | 19, 1094 | 5, 1095 | 34, 1096 | 12, 1097 | 25, 1098 | 25, 1099 | 24, 1100 | 21, 1101 | 37, 1102 | 76, 1103 | 75, 1104 | 46, 1105 | 13, 1106 | 22, 1107 | 40, 1108 | 45, 1109 | 63, 1110 | 63, 1111 | 77, 1112 | 34, 1113 | 29, 1114 | 50, 1115 | 50, 1116 | 37, 1117 | 72, 1118 | 33, 1119 | 30, 1120 | 43, 1121 | 65, 1122 | 66, 1123 | 63, 1124 | 2, 1125 | 15, 1126 | 82, 1127 | 72, 1128 | 82, 1129 | 39, 1130 | 8, 1131 | 81, 1132 | 47, 1133 | 28, 1134 | 74, 1135 | 16, 1136 | 64, 1137 | 37, 1138 | 85, 1139 | 85, 1140 | 46, 1141 | 56, 1142 | 46, 1143 | 37, 1144 | 32, 1145 | 81, 1146 | 77, 1147 | 36 1148 | ] 1149 | } 1150 | ] 1151 | } 1152 | --------------------------------------------------------------------------------