├── .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 |
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 | | Name |
25 |
26 |
27 | | Name |
28 |
29 |
30 | | Name |
31 |
32 |
33 | | Name |
34 |
35 |
36 | | Name |
37 |
38 |
39 |
40 |
41 |
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 |
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...4>
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...4>
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 |
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 |
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 |
--------------------------------------------------------------------------------