Jon West | 197faff | 2019-05-02 18:27:18 -0400 | [diff] [blame] | 1 | console.clear(); |
| 2 | |
| 3 | /** |
| 4 | * Choose Your Own Adventure-style data structure. |
| 5 | * |
| 6 | * Works on the principle of 'entries'/'chapters', like the oldschool 'turn to 100' |
| 7 | * style Fighting Fantasy gamebooks - this is *not* a proper text adventure engine |
| 8 | * with full location, inventory and state management. It could probably be adapted |
| 9 | * to that quite easily, though. |
| 10 | * |
| 11 | * Definition format: |
| 12 | * |
| 13 | * Content: |
| 14 | * - id: unique string ID for use in 'goto'/'next'. |
| 15 | * - text: body text for this entry. |
| 16 | * - extra: array of additional paragraphs: |
| 17 | * - text: text for this paragraph |
| 18 | * - requires: string/array of item(s) required for this paragraph |
| 19 | * to be included. Use '!itemname' to invert logic. |
| 20 | * |
| 21 | * Inventory / state: |
| 22 | * - gives: string/array of item(s) gained when entry is visited |
| 23 | * - takes: string/array of item(s) lost when entry is visited |
| 24 | * - gameover: if 'win' or 'lose', changes game state when entry is visited |
| 25 | * |
| 26 | * Navigation (ONE of the following): |
| 27 | * - next: id of next entry (will convert to a single 'Continue...' option) |
| 28 | * - options: array of options for this entry: |
| 29 | * - text: text used for option |
| 30 | * - goto: id of entry this option leads to |
| 31 | * - requires: string/array of item(s) required for this option |
| 32 | * to be available. Use '!itemname' to invert logic. |
| 33 | * |
| 34 | **/ |
| 35 | |
| 36 | var ENTRIES = [{ |
| 37 | // Recreation room |
| 38 | id: 'e_recroom', |
| 39 | text: 'You are in a small-ish room with a computer in front of you ' + |
| 40 | 'The computer screen shows a terminal, and on the terminal ' + |
| 41 | 'error showing that you need to repo init before running repo ' + |
| 42 | 'sync. What do you do', |
| 43 | extra: [{ |
| 44 | text: 'You have the BlissROMs thread for a different device ' + |
| 45 | 'open on a second screen.', |
| 46 | }], |
| 47 | options: [{ |
| 48 | text: 'Examine the webpage for the GitHub link', |
| 49 | goto: 'e_github' |
| 50 | }, { |
| 51 | text: 'Search Google for your answer', |
| 52 | goto: 'e_google' |
| 53 | }], |
| 54 | start: true |
| 55 | }, { |
| 56 | // Look at the plushie |
| 57 | id: 'e_github', |
| 58 | text: 'Upon closer inspection, the webpage has a section for Sources, Links, and ' + |
| 59 | 'Social Media. You scan through them quickly to see if anything could be useful ' + |
| 60 | 'What section do you decide on', |
| 61 | options: [{ |
| 62 | text: 'Copy the link for the BlissROMs github', |
| 63 | goto: 'e_got_github_link' |
| 64 | }, { |
| 65 | text: 'Open a new tab and go to Google Search instead', |
| 66 | goto: 'e_google' |
| 67 | }] |
| 68 | }, { |
| 69 | // Get the mask off the plushie |
| 70 | id: 'e_got_github_link', |
| 71 | text: 'You grab the link to the BlissROMs GitHub page, and paste it into a new tab. It loads and shows ' + |
| 72 | 'a list of repo links is shown, and you are a little overwhelmed by the amount of choices available. ' + |
| 73 | 'You copy the link to the manifest repo, but are unsure. So you decide to head to Google instead ', |
| 74 | next: 'e_google' |
| 75 | }, { |
| 76 | // Main atrium |
| 77 | id: 'e_google', |
| 78 | text: 'You sit there, staring at the Google Search page. ' + |
| 79 | 'As you type in BlissROMs repo init, the page results spit out you notice a few things in the ' + |
| 80 | 'list that look familiar. The top result is for the manifest link, while the second result is for ' + |
| 81 | 'a how-to unlock and root any device. The third link shown is for a YouTube video on building ' + |
| 82 | 'Android ROMs, and the fourth link down leads to the BlissROMs webpage ', |
| 83 | options: [{ |
| 84 | text: 'Follow the think to the BlissROMs Manifest.', |
| 85 | goto: 'e_link_manifest' |
| 86 | }, { |
| 87 | text: 'Follow the link to unlock and root your device', |
| 88 | goto: 'e_link_droid' |
| 89 | }, { |
| 90 | text: 'Head to YouTube for the video on building Android ROMs', |
| 91 | goto: 'e_link_youtube' |
| 92 | }, { |
| 93 | text: 'Head over to the BlissROMs webpage', |
| 94 | goto: 'e_link_blissroms' |
| 95 | }], |
| 96 | }, { |
| 97 | // Manifest link |
| 98 | id: 'e_link_manifest', |
| 99 | text: 'You head to the manifest page and it shows a few files on the top, followed by a descriptive ' + |
| 100 | 'readme detailing all the steps needed to install dependencies, repo init, repo sync and even ' + |
| 101 | 'steps to build the ROM, GSI, EFI & x86. You remember that you were looking for the init link ' + |
| 102 | 'so you copy that text snippet. ', |
| 103 | next: 'e_back_to_terminal' |
| 104 | }, { |
| 105 | // Killed by malware |
| 106 | id: 'e_link_droid', |
| 107 | text: 'You follow the link to unlock and root your device. Upon loading the page, a pop-up dialog shows ' + |
| 108 | 'and mentioned that you need to click OK in order to root your device. You click it, and the screen ' + |
| 109 | 'displays a ton of terminal windows opening and closing. You smell smoke from the PC, and before you ' + |
| 110 | 'can reach the power button, the machine shuts off. smoke is still pouring from the case. ' + |
| 111 | ' You have failed, go back to your iPhone looser', |
| 112 | gameover: 'lose' |
| 113 | }, { |
| 114 | // Killed by youtube |
| 115 | id: 'e_link_youtube', |
| 116 | text: 'You follow the link to watch the YouTube video on building Android, and start to soak up all the ' + |
| 117 | 'info provided. At the one hour mark, you remember that you left the meatloaf in the oven, and you ' + |
| 118 | 'are starting to smell a combination of burnt BBQ sauce and beef. You rush to the kitchen to find ' + |
| 119 | 'half the kitchen on fire already. You reach for a fire extinguisher and try to use it. There was no ' + |
| 120 | 'pressure in the extinguisher. You then remember seeing a YouTube video on extinguishing a fire with ' + |
| 121 | 'water, so you quickly throw a bowl of water on the flames. ' + |
| 122 | 'The flames quickly grow because you threw water on a grease fire and you die. Dont believe everything ' + |
| 123 | 'you see on youtube ', |
| 124 | gameover: 'lose' |
| 125 | }, { |
| 126 | // BlissROMs link |
| 127 | id: 'e_link_blissroms', |
| 128 | text: 'You load the BlissROMs page, and scroll through it. At the bottom, you notice the BlissROMs github ' + |
| 129 | 'link from earlier. You follow it and are shown a list of repos. ', |
| 130 | extra: [{ |
| 131 | text: 'You study your options and notice there are a few repos pinned to the top for manifest, ' + |
| 132 | 'frameworks/base, vendor/bliss, etc. ', |
| 133 | }], |
| 134 | options: [{ |
| 135 | text: 'Check out the manifest repo', |
| 136 | goto: 'e_link_manifest' |
| 137 | }, { |
| 138 | text: 'Check out frameworks/base repo.', |
| 139 | goto: 'e_link_fwb' |
| 140 | }, { |
| 141 | text: 'Check out the vendor/bliss repo', |
| 142 | goto: 'e_vendor_bliss' |
| 143 | }] |
| 144 | }, { |
| 145 | // Link fwb |
| 146 | id: 'e_link_fwb', |
| 147 | text: 'You click the link to frameworks/base, and are presented with a page of source files. and code. None of ' + |
| 148 | 'this looks like something you are looking for. So you decide to go back to the main repo page' , |
| 149 | next: 'e_link_github_again' |
| 150 | }, { |
| 151 | // Link vendor/bliss |
| 152 | id: 'e_vendor_bliss', |
| 153 | text: 'You click the link to vendor/bliss, and are presented with a page of source files. and code. None of ' + |
| 154 | 'this looks like something you are looking for. So you decide to go back to the main repo page' , |
| 155 | next: 'e_link_github_again' |
| 156 | }, { |
| 157 | // GitHub again |
| 158 | id: 'e_link_github_again', |
| 159 | text: 'You head back to the BlissROMs github link from earlier. You see the same list of repos. ', |
| 160 | extra: [{ |
| 161 | text: 'You study your options and notice there are a few repos pinned to the top for manifest, ' + |
| 162 | 'frameworks/base, vendor/bliss, etc. ', |
| 163 | }], |
| 164 | options: [{ |
| 165 | text: 'Check out the manifest repo', |
| 166 | goto: 'e_link_manifest' |
| 167 | }, { |
| 168 | text: 'Check out frameworks/base repo.', |
| 169 | goto: 'e_link_fwb' |
| 170 | }, { |
| 171 | text: 'Check out the vendor/bliss repo', |
| 172 | goto: 'e_vendor_bliss' |
| 173 | }] |
| 174 | }, { |
| 175 | // Back to terminal |
| 176 | id: 'e_back_to_terminal', |
| 177 | text: 'In the terminal window, you take the text snippet you grabbed and paste it into the terminal ' + |
| 178 | 'window, then hit Return. It spits out a bunch of text and presents you with a message that reads ' + |
| 179 | 'Repo has been successfully initiated. ', |
| 180 | options: [{ |
| 181 | text: 'View the Github manifest page for the next step', |
| 182 | goto: 'e_manifest_next' |
| 183 | }, { |
| 184 | text: 'Enter repo sync into the terminal', |
| 185 | goto: 'e_repo_sync' |
| 186 | }, { |
| 187 | text: 'Search Google for an answer', |
| 188 | goto: 'e_google_2' |
| 189 | }, { |
| 190 | text: 'Search Youtube for an answer', |
| 191 | goto: 'e_link_youtube' |
| 192 | }] |
| 193 | }, { |
| 194 | // Google again |
| 195 | id: 'e_google_2', |
| 196 | text: 'You head back to google to find out what you do next, so you search for blissroms repo init and ' + |
| 197 | 'are presented a few options. The top two links lead to the BlissROMs github page, so you follow that ', |
| 198 | next: 'e_link_github_again' |
| 199 | },{ |
| 200 | // manifest |
| 201 | id: 'e_manifest_next', |
| 202 | text: 'You return to the manifest repo, and scroll down to the list of instructions. Once there, you notice ' + |
| 203 | ' that the next item after the repo init part are about repo sync. ' + |
| 204 | 'What do you decide to do', |
| 205 | options: [{ |
| 206 | text: 'Copy the command for repo sync', |
| 207 | goto: 'e_repo_sync' |
| 208 | }, { |
| 209 | text: 'Give up and go watch some TV', |
| 210 | goto: 'e_go_watch_tv' |
| 211 | }, { |
| 212 | text: 'Leave and go outside.', |
| 213 | goto: 'e_outside' |
| 214 | }] |
| 215 | }, { |
| 216 | // repo sync |
| 217 | id: 'e_repo_sync', |
| 218 | text: 'You head back to the terminal and enter the repo sync command into it, and press return ' + |
| 219 | 'The screen lights up with hundreds of lines of text scrolling past the screen endlessly. ' + |
| 220 | 'After about 30 min, the repo sync finishes and you are presented with a blinking cursor. ' + |
| 221 | 'What do you do now', |
| 222 | options: [{ |
| 223 | text: 'Head back to the Bliss manifest page to see whats next', |
| 224 | goto: 'e_go_build' |
| 225 | }, { |
| 226 | text: 'Give up and go watch some TV', |
| 227 | goto: 'e_go_watch_tv' |
| 228 | }, { |
| 229 | text: 'Leave and go outside', |
| 230 | goto: 'e_outside' |
| 231 | }] |
| 232 | },{ |
| 233 | // build |
| 234 | id: 'e_go_build', |
| 235 | text: 'You head back to the Bliss manifest repo and follow the instructions a little further until ' + |
| 236 | 'you find the section for building. ' + |
| 237 | 'After finding the section, you see a code snippet to initiate the lunch command and build.' + |
| 238 | 'What do you do now', |
| 239 | options: [{ |
| 240 | text: 'Leave and go outside', |
| 241 | goto: 'e_outside' |
| 242 | }, { |
| 243 | text: 'Copy the code to the terminal window and press enter', |
| 244 | goto: 'e_go_compile' |
| 245 | }, { |
| 246 | text: 'Give up and go watch some TV', |
| 247 | goto: 'e_go_watch_tv' |
| 248 | }] |
| 249 | }, { |
| 250 | // Die outside |
| 251 | id: 'e_outside', |
| 252 | text: 'You decide to give up for now and go outside. Once there, you are confronted with a huge thunderstorm ' + |
| 253 | ' and lightning clashes and catches your house on fire.' , |
| 254 | gameover: 'lose' |
| 255 | }, { |
| 256 | // Die watching TV |
| 257 | id: 'e_go_watch_tv', |
| 258 | text: 'You decide to give up for now and go watch some TV. Once the TV is turned on, an alien mothership enters ' + |
| 259 | 'earths atmosphere and takes over all TV broadcasts. They state that the people of earth have 30 seconds to ' + |
| 260 | 'say their prayers before they git the reset button on human life. You waste the last 30 seconds of your life ' + |
| 261 | 'by flipping through Facebook on your phone. ', |
| 262 | gameover: 'lose' |
| 263 | }, |
| 264 | { |
| 265 | // WINNING |
| 266 | id: 'e_go_compile', |
| 267 | text: 'You copy the lunch command into the terminal, then press return, and select the number representing your ' + |
| 268 | 'device. The build launches and you safely go watch TV for a bit while things are compiling ', |
| 269 | gameover: 'win', |
| 270 | }, |
| 271 | |
| 272 | ]; |
| 273 | |
| 274 | /** |
| 275 | * Parser module for the data format. |
| 276 | * Reads the data object format and creates an internal copy with required |
| 277 | * transformations and parsing. Exposes methods to start/reset the game, |
| 278 | * advance the game via choices/actions, and read the currently active entry. |
| 279 | * |
| 280 | * The module is just data-driven, and returns objects from its methods; it |
| 281 | * does no handling of game display or user input directly. It needs a frontend |
| 282 | * written for it in order for a player to interact with it. |
| 283 | **/ |
| 284 | var CYOA = (function() { |
| 285 | |
| 286 | var ENTRY_DATA, |
| 287 | currentEntryId, currentEntryData, |
| 288 | inventory; |
| 289 | |
| 290 | function _init(entryData) { |
| 291 | // clear state |
| 292 | ENTRY_DATA = {}; |
| 293 | currentEntryId = null; |
| 294 | currentEntryData = {}; |
| 295 | inventory = []; |
| 296 | |
| 297 | var startEntryId = null; |
| 298 | |
| 299 | // Parse entry data into internal object |
| 300 | entryData.forEach(function(entry) { |
| 301 | ENTRY_DATA[entry.id] = Object.create(entry); |
| 302 | |
| 303 | // Track the starting entry and warn of duplicates |
| 304 | if (entry.start === true) { |
| 305 | if (startEntryId !== null) { |
| 306 | console.error('More than one starting state defined:', startEntryId, entry.id); |
| 307 | } else { |
| 308 | startEntryId = entry.id; |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | // Process extra paragraphs if present |
| 313 | if (entry.extra) { |
| 314 | entry.extra.forEach(function(ext) { |
| 315 | // convert string options to single-item arrays for easier parsing |
| 316 | if (ext.requires && (typeof ext.requires === 'string')) { |
| 317 | ext.requires = [ext.requires]; |
| 318 | } |
| 319 | }); |
| 320 | } |
| 321 | |
| 322 | // 'Next' overrides all other options |
| 323 | if (entry.next) { |
| 324 | entry.options = [{ |
| 325 | text: 'Continue...', |
| 326 | goto: entry.next |
| 327 | }]; |
| 328 | } |
| 329 | // Process and validate options |
| 330 | if (entry.options) { |
| 331 | entry.options.forEach(function(opt) { |
| 332 | // options must have a 'goto' |
| 333 | if (!opt.goto) console.error('Entry', entry.id, ' has option without a goto: ', opt.text); |
| 334 | // convert string options to single-item arrays for easier parsing |
| 335 | if (opt.requires && (typeof opt.requires === 'string')) { |
| 336 | opt.requires = [opt.requires]; |
| 337 | } |
| 338 | }); |
| 339 | } |
| 340 | }); |
| 341 | |
| 342 | // Set initial state from starting entry |
| 343 | if (startEntryId === null) console.error('No start entry found'); |
| 344 | _setEntry(startEntryId); |
| 345 | } |
| 346 | |
| 347 | // Inventory methods (accept string or array) |
| 348 | |
| 349 | function _addToInventory(items) { |
| 350 | if (typeof items === 'string') items = [items]; |
| 351 | inventory = inventory.concat(items); |
| 352 | } |
| 353 | |
| 354 | function _takeFromInventory(items) { |
| 355 | if (typeof items === 'string') items = [items]; |
| 356 | var newInv = []; |
| 357 | inventory.forEach(function(item) { |
| 358 | if (items.indexOf(item) === -1) newInv.push(item); |
| 359 | }); |
| 360 | inventory = newInv; |
| 361 | } |
| 362 | |
| 363 | function _checkInventory(item) { |
| 364 | return (inventory.indexOf(item) > -1); |
| 365 | } |
| 366 | |
| 367 | // Utility method to check a 'requires'-format array against the current inventory |
| 368 | function _hasRequirements(opt) { |
| 369 | var isAvailable = true; |
| 370 | if (opt.requires) { |
| 371 | opt.requires.forEach(function(req) { |
| 372 | if (req.charAt(0) === '!' && _checkInventory(req.substr(1))) isAvailable = false; |
| 373 | if (req.charAt(0) !== '!' && !_checkInventory(req)) isAvailable = false; |
| 374 | }); |
| 375 | } |
| 376 | return isAvailable; |
| 377 | } |
| 378 | |
| 379 | // Updates the current entry data to the given entry ID. |
| 380 | // Composes the current entry data based on conditionals set in the entry data, |
| 381 | // including required inventory to display options, etc. |
| 382 | // Also makes changes to inventory and state based on the definition data. |
| 383 | function _setEntry(id) { |
| 384 | if (!id in ENTRY_DATA) console.error('Unable to change entry: invalid entry id', id); |
| 385 | currentEntryId = id; |
| 386 | |
| 387 | var data = ENTRY_DATA[id]; |
| 388 | currentEntryData = { |
| 389 | id: data.id, |
| 390 | text: data.text, |
| 391 | extra: [] |
| 392 | }; |
| 393 | |
| 394 | // Add/remove inventory items in this entry |
| 395 | if (data.gives) _addToInventory(data.gives); |
| 396 | if (data.takes) _takeFromInventory(data.takes); |
| 397 | |
| 398 | // Update text with extras |
| 399 | if (data.extra) { |
| 400 | data.extra.forEach(function(ext) { |
| 401 | if (_hasRequirements(ext)) currentEntryData.extra.push(ext.text); |
| 402 | }); |
| 403 | } |
| 404 | |
| 405 | // State modifiers |
| 406 | // TODO: make this more definitive and mutate options accordingly |
| 407 | if (data.gameover) currentEntryData.gameover = data.gameover; |
| 408 | |
| 409 | // Define available options based on inventory requirements |
| 410 | if (data.options) { |
| 411 | currentEntryData.options = []; |
| 412 | data.options.forEach(function(opt, idx) { |
| 413 | if (_hasRequirements(opt)) { |
| 414 | currentEntryData.options.push({ |
| 415 | text: opt.text, |
| 416 | goto: opt.goto |
| 417 | }); |
| 418 | } |
| 419 | }); |
| 420 | } |
| 421 | return currentEntryData; |
| 422 | } |
| 423 | |
| 424 | function startGame(data) { |
| 425 | _init(data); |
| 426 | } |
| 427 | |
| 428 | function getCurrentEntry() { |
| 429 | if (currentEntryData === {}) console.error('No current entry; has the game started?'); |
| 430 | return currentEntryData; |
| 431 | } |
| 432 | |
| 433 | function getInventory() { |
| 434 | return inventory; |
| 435 | } |
| 436 | |
| 437 | // Changes the active entry according to the numeric ID of the option passed in, |
| 438 | // if it is present in the current entry. |
| 439 | function doOption(idx) { |
| 440 | if (!currentEntryData.options) console.error('Can not complete option', idx); |
| 441 | var opt = currentEntryData.options[idx]; |
| 442 | var newEntryId = opt.goto; |
| 443 | if (!newEntryId in ENTRY_DATA) console.error('Cannot do option: invalid goto id', newEntryId); |
| 444 | return _setEntry(newEntryId); |
| 445 | } |
| 446 | |
| 447 | return { |
| 448 | startGame: startGame, |
| 449 | getCurrentEntry: getCurrentEntry, |
| 450 | getInventory: getInventory, |
| 451 | doOption: doOption |
| 452 | }; |
| 453 | })(); |
| 454 | |
| 455 | /** |
| 456 | * Some simple jQuery DOM logic for demo purposes. |
| 457 | * This could easily be expanded for better presentation, |
| 458 | * per-location graphics, all kinds of stuff. |
| 459 | **/ |
| 460 | var Game = (function() { |
| 461 | |
| 462 | var DATA; |
| 463 | |
| 464 | // Container element to render into |
| 465 | var $el = $('#output_a'); |
| 466 | |
| 467 | // Text for game over scenarios |
| 468 | var endMsgs = { |
| 469 | win: 'You won! Play again...', |
| 470 | lose: 'You failed. Restart...' |
| 471 | }; |
| 472 | |
| 473 | // Reads the current entry data and puts DOM nodes |
| 474 | // in the container to display the text and options |
| 475 | function render(isStart) { |
| 476 | var d = CYOA.getCurrentEntry(); |
| 477 | |
| 478 | // Clear the container and write the body text |
| 479 | $el.html(''); |
| 480 | if (isStart) $el.append('<p class="title">*** Build BlissROM ***</p>'); |
| 481 | $el.append('<p>' + d.text + '</p>'); |
| 482 | |
| 483 | d.extra.forEach(function(ext) { |
| 484 | $el.append('<p>' + ext + '</p>'); |
| 485 | }); |
| 486 | |
| 487 | // Write out a list of option or restart links in a list |
| 488 | // (click handlers bound in init() will handle these) |
| 489 | var $opts = $('<ul/>'); |
| 490 | if (d.gameover) { |
| 491 | var $action = $('<li><a class="opt gameover ' + d.gameover + '" href="#">' + |
| 492 | endMsgs[d.gameover] + '</a></li>'); |
| 493 | $opts.append($action); |
| 494 | } |
| 495 | if (d.options) { |
| 496 | d.options.forEach(function(opt, idx) { |
| 497 | var $opt = $('<li><a class="opt action" href="#" data-opt="' + idx + '">' + |
| 498 | opt.text + '</a></li>'); |
| 499 | $opts.append($opt); |
| 500 | }); |
| 501 | } |
| 502 | $el.append($opts); |
| 503 | |
| 504 | // Show current inventory |
| 505 | if (!d.gameover) { |
| 506 | var inv = CYOA.getInventory(); |
| 507 | if (inv.length) { |
| 508 | $el.append('<p class="inv">You are carrying: ' + inv.join(', ') + '</p>'); |
| 509 | } |
| 510 | } |
| 511 | } |
| 512 | |
| 513 | function init(entryData) { |
| 514 | |
| 515 | DATA = entryData; |
| 516 | |
| 517 | // Click handlers for option links. Bound to the document |
| 518 | // as we destroy and rebuild the links per-entry. |
| 519 | $(document).on('click', '.action', function(e) { |
| 520 | e.preventDefault(); |
| 521 | var opt = $(this).attr('data-opt'); |
| 522 | console.log('do option', opt); |
| 523 | if (CYOA.doOption(opt)) render(); |
| 524 | }); |
| 525 | |
| 526 | // As above but for win/lose links. Restart the game when used |
| 527 | $(document).on('click', '.gameover', function(e) { |
| 528 | e.preventDefault(); |
| 529 | _start(); |
| 530 | }); |
| 531 | |
| 532 | _start(); |
| 533 | } |
| 534 | |
| 535 | function _start() { |
| 536 | // Init the game and render the first entry |
| 537 | CYOA.startGame(DATA); |
| 538 | render(true); |
| 539 | } |
| 540 | |
| 541 | return { |
| 542 | init: init |
| 543 | } |
| 544 | |
| 545 | })(); |
| 546 | |
| 547 | // Kick off |
| 548 | Game.init(ENTRIES); |