master
Evan Powell 4 years ago
commit e1b1f6af20

@ -20,6 +20,17 @@ The easiest way to run this project is by creating a basic static web server usi
This will start a web server on port 8000. You can then run the game by navigating to http://localhost:8000/ from a web browser.
## Documentation
You can [preview it here](https://htmlpreview.github.io/?https://raw.githubusercontent.com/EECS-448-Battleship/project-1/master/documentation/generated/index.html). Otherwise, it is generated by JSDoc in the `documentation/generated` directory.
### Re-generating the documentation
To regenerate the docs, you need Node.js and the Yarn package manager installed. Then, just:
```shell script
cd documentation
./generate.sh
```
## Contributors
- Lucas Brakenridge
- Javier Barea Lara

@ -0,0 +1 @@
node_modules

@ -0,0 +1,51 @@
# Battleship
## EECS 448 - Project 1
This is a basic battleship game created as our submission for project 1 for EECS 448 at the University of Kansas.
## Structure Info
This project has been wired up to use Vue.js to help organize components of the game.
These components are defined in files that end in the `.component.js` extension, and are located in the `src/components/` directory.
The entry point for the project is the `index.html`. This file contains the basic logic for loading Vue, and adding the game board to the page.
Obviously, we'll flesh out the look-and-feel as we go along. This is just a basic starter for now.
## How to Run
The easiest way to run this project is by creating a basic static web server using Python. This is super simple:
1. Open a terminal or command prompt to the root of this project (i.e. the directory this file is in).
2. Start the server: `python -m http.server`
This will start a web server on port 8000. You can then run the game by navigating to http://localhost:8000/ from a web browser.
## Documentation
You can [preview it here](https://htmlpreview.github.io/?https://raw.githubusercontent.com/EECS-448-Battleship/project-1/master/documentation/generated/index.html). Otherwise, it is generated by JSDoc in the `documentation/generated` directory.
### Re-generating the documentation
To regenerate the docs, you need Node.js and the Yarn package manager installed. Then, just:
```shell script
cd documentation
./generate.sh
```
## Third-Party Libraries
The files in the `lib/` are external libraries used in this project.
- Vue.js
- A front-end framework. Used under the terms of the MIT license.
- https://github.com/vuejs/vue
- VuES6.js
- A kind-of crappy loader for defining Vue components using ES6 classes.
- Also used under the terms of the MIT license.
- https://code.garrettmills.dev/garrettmills/vues6
- Sound effects obtained from https://www.zapsplat.com and used with permission.
## Contributors
- Lucas Brakenridge
- Javier Barea Lara
- Garrett Mills
- Evan Powell
- Alec Horlick-Mills

@ -0,0 +1,3 @@
#!/bin/sh
yarn install
./node_modules/.bin/jsdoc --destination ./generated --readme ./README.md --recurse ../src

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,621 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Class: GridCellComponent</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Class: GridCellComponent</h1>
<section>
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>GridCellComponent<span class="signature">()</span><span class="type-signature"></span></h2>
<div class="class-description">A component which represents a single, programmable grid cell.</div>
</header>
<article>
<div class="container-overview">
<h2>Constructor</h2>
<h4 class="name" id="GridCellComponent"><span class="type-signature"></span>new GridCellComponent<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_GridCell.component.js.html">components/GridCell.component.js</a>, <a href="components_GridCell.component.js.html#line22">line 22</a>
</li></ul></dd>
</dl>
</div>
<h3 class="subsection-title">Extends</h3>
<ul>
<li>Component</li>
</ul>
<h3 class="subsection-title">Members</h3>
<h4 class="name" id=".props"><span class="type-signature">(static) </span>props<span class="type-signature"></span></h4>
<div class="description">
Properties that can be passed into this component.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_GridCell.component.js.html">components/GridCell.component.js</a>, <a href="components_GridCell.component.js.html#line27">line 27</a>
</li></ul></dd>
</dl>
<h4 class="name" id="GridCellState"><span class="type-signature"></span>GridCellState<span class="type-signature"></span></h4>
<div class="description">
Make the "GridCellState" enum available in the template.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_GridCell.component.js.html">components/GridCell.component.js</a>, <a href="components_GridCell.component.js.html#line35">line 35</a>
</li></ul></dd>
</dl>
<h3 class="subsection-title">Methods</h3>
<h4 class="name" id="on_click"><span class="type-signature"></span>on_click<span class="signature">()</span><span class="type-signature"></span></h4>
<div class="description">
Fire a click event.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_GridCell.component.js.html">components/GridCell.component.js</a>, <a href="components_GridCell.component.js.html#line40">line 40</a>
</li></ul></dd>
</dl>
<h4 class="name" id="on_hover"><span class="type-signature"></span>on_hover<span class="signature">($event)</span><span class="type-signature"></span></h4>
<div class="description">
Fire a hover event.
</div>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>$event</code></td>
<td class="type">
</td>
<td class="description last"></td>
</tr>
</tbody>
</table>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_GridCell.component.js.html">components/GridCell.component.js</a>, <a href="components_GridCell.component.js.html#line48">line 48</a>
</li></ul></dd>
</dl>
<h4 class="name" id="on_mouse_leave"><span class="type-signature"></span>on_mouse_leave<span class="signature">()</span><span class="type-signature"></span></h4>
<div class="description">
Fire a "hoverchange" event.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_GridCell.component.js.html">components/GridCell.component.js</a>, <a href="components_GridCell.component.js.html#line55">line 55</a>
</li></ul></dd>
</dl>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,821 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Class: ScoreBoardComponent</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Class: ScoreBoardComponent</h1>
<section>
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>ScoreBoardComponent<span class="signature">()</span><span class="type-signature"></span></h2>
<div class="class-description">A component which represents the programmable scoreboard.</div>
</header>
<article>
<div class="container-overview">
<h2>Constructor</h2>
<h4 class="name" id="ScoreBoardComponent"><span class="type-signature"></span>new ScoreBoardComponent<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line35">line 35</a>
</li></ul></dd>
</dl>
</div>
<h3 class="subsection-title">Extends</h3>
<ul>
<li>Component</li>
</ul>
<h3 class="subsection-title">Members</h3>
<h4 class="name" id="current_player"><span class="type-signature"></span>current_player<span class="type-signature"> :string|undefined</span></h4>
<div class="description">
The current player.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">string</span>
|
<span class="param-type">undefined</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line68">line 68</a>
</li></ul></dd>
</dl>
<h4 class="name" id="player_one_progress"><span class="type-signature"></span>player_one_progress<span class="type-signature"> :number</span></h4>
<div class="description">
The progress of player one, as a decimal.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">number</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line56">line 56</a>
</li></ul></dd>
</dl>
<h4 class="name" id="player_one_score"><span class="type-signature"></span>player_one_score<span class="type-signature"> :number</span></h4>
<div class="description">
The score of player one.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">number</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line44">line 44</a>
</li></ul></dd>
</dl>
<h4 class="name" id="player_two_progress"><span class="type-signature"></span>player_two_progress<span class="type-signature"> :number</span></h4>
<div class="description">
The progress of player two, as a decimal.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">number</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line62">line 62</a>
</li></ul></dd>
</dl>
<h4 class="name" id="player_two_score"><span class="type-signature"></span>player_two_score<span class="type-signature"> :number</span></h4>
<div class="description">
The score of player two.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">number</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line50">line 50</a>
</li></ul></dd>
</dl>
<h4 class="name" id="winning_player"><span class="type-signature"></span>winning_player<span class="type-signature"> :string|undefined</span></h4>
<div class="description">
The winning player.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">string</span>
|
<span class="param-type">undefined</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line74">line 74</a>
</li></ul></dd>
</dl>
<h3 class="subsection-title">Methods</h3>
<h4 class="name" id="update"><span class="type-signature"></span>update<span class="signature">()</span><span class="type-signature"></span></h4>
<div class="description">
Fetch new data from the game service.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line93">line 93</a>
</li></ul></dd>
</dl>
<h4 class="name" id="vue_on_create"><span class="type-signature">(async) </span>vue_on_create<span class="signature">()</span><span class="type-signature"> &rarr; {Promise.&lt;void>}</span></h4>
<div class="description">
Called when the component is initialized.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="components_ScoreBoard.component.js.html">components/ScoreBoard.component.js</a>, <a href="components_ScoreBoard.component.js.html#line82">line 82</a>
</li></ul></dd>
</dl>
<h5>Returns:</h5>
<dl>
<dt>
Type
</dt>
<dd>
<span class="param-type">Promise.&lt;void></span>
</dd>
</dl>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,287 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: components/GameBoard.component.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: components/GameBoard.component.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import {Component} from '../../lib/vues6.js'
import {ShipType, isShipCell} from '../module/util.js'
import game_service from '../services/GameState.service.js'
/*
* This is the HTML/JavaScript for the game board component.
* The "template" variable at the top defines the HTML for this component. It can contain references
* to methods and properties on the "GameBoardComponent" class shown below.
*
* For example, the "greeting" property is referenced in the template as "{{ greeting }}". When
* the page loads, this will be replaced by the value of the "greeting" property.
*
* The class below manages the logic referenced by the template. Then, we can use the component
* in the application by creating an HTML tag with the value of the "selector()" getter.
*
* In this case, that's the &lt;app-game-board>&lt;/app-game-board> tag in index.html.
*
* We can also use components w/in components, to keep code clean. For example, we could have
* a battleship component that we reference inside this game board component.
*
* Battleship grid is 14x14.
*/
const template = `
&lt;div class="game-board-component" v-if="ready" @mouseleave="on_mouse_leave">
&lt;div class="grid-container">
&lt;div class="grid-row" v-for="(row,i) of rows">
&lt;br> &lt;span class="label">{{ i + 1 }}&lt;/span>
&lt;app-game-cell
v-for="(cell,j) of row"
v-bind:render="cell.render"
@click="on_cell_click(i,j)"
@hover="on_cell_hover(i,j)"
v-bind:has_ghost_ship="is_ghost_cell(i,j)"
>&lt;/app-game-cell>
&lt;/div>
&lt;div class="column_labels">
&lt;div class="label" v-for="(label,i) of column_labels">{{ label }}&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
`
/**
* A component which represents a single, programmable game board.
* @extends Component
*/
class GameBoardComponent extends Component {
static get selector() { return 'app-game-board' }
static get template() { return template }
static get props() { return ['rows', 'is_placement_mode', 'ships_to_place', 'is_missile_mode'] }
/**
* If true, the grid is ready to be rendered. If false,
* the grid will be hidden.
* @type {boolean}
*/
ready = false
/**
* The various column labels to display.
* @type {string[]}
*/
column_labels = ["A", "B", "C", "D", "E", "F", "G", "H", "I"]
/**
* Array of coordinates as [row_index, column_index] of cells which should
* show a ghost ship overlay.
* @type {number[]}
*/
ship_ghost_cells = []
/**
* The ship currently being placed.
* @type {string}
*/
current_placement = ShipType.x3
/**
* Set to true when the shift key is pressed.
* @type {boolean}
*/
shift_pressed = false
/**
* Array of functions bound to event listeners. Used to
* remove event listeners on destroy.
* @type {function[]}
*/
bound_fns = []
/**
* Called when the component is initialized.
* @return {Promise&lt;void>}
*/
async vue_on_create() {
this.ready = true
// We need to listen for keyup/keydown so we can tell when the user has
// pressed/released the shift key.
const keyup_fn = this.on_keyup.bind(this)
const keydown_fn = this.on_keydown.bind(this)
this.bound_fns.push(keyup_fn, keydown_fn)
window.addEventListener('keyup', keyup_fn)
window.addEventListener('keydown', keydown_fn)
}
/**
* Called when the component is destroyed.
* @return {Promise&lt;void>}
*/
async vue_on_destroy() {
// Remove the event listeners for the shift key
const [keyup_fn, keydown_fn] = this.bound_fns
window.removeEventListener('keyup', keyup_fn)
window.removeEventListener('keydown', keydown_fn)
}
/**
* Called when a user clicks a cell. If in placement mode, will attempt to place
* a ship. If in missile mode, will attempt to fire a missile.
* @param {number} row_i - the index of the row
* @param {number} cell_i - the index of the cell
*/
on_cell_click(row_i, cell_i) {
if ( this.is_placement_mode &amp;&amp; this.ships_to_place[0] ) {
// We should try to place a ship here
if ( this.ship_ghost_cells.length > 0 ) {
// We have some valid ship placement coordinates
const coord_one = this.ship_ghost_cells[0]
const coord_two = this.ship_ghost_cells.slice(-1)[0]
game_service.place_ship(this.ships_to_place[0], coord_one, coord_two)
this.$emit('shipplaced')
}
} else if ( this.is_missile_mode ) {
this.$emit('missilefired', [row_i, cell_i])
}
}
/**
* Called when the user hovers over a cell.
* When in placement mode, this updates the cells that show the ghost ship.
* @param {number} row_i
* @param {number} cell_i
*/
on_cell_hover(row_i, cell_i) {
if ( this.is_placement_mode ) {
// If we're in placement mode, determine if the cell the user is hovering
// over is a valid place to place the ship.
const ghost_cells = [[row_i, cell_i]]
const is_horizontal = this.shift_pressed
let is_valid_hover = true
if ( !is_horizontal ) {
const num_cells = game_service.get_ship_length(this.ships_to_place[0])
for ( let i = row_i + 1; i &lt; row_i + num_cells; i += 1 ) {
ghost_cells.push([i, cell_i])
if ( i > 8 ) is_valid_hover = false
}
} else {
const num_cells = game_service.get_ship_length(this.ships_to_place[0])
for ( let i = cell_i + 1; i &lt; cell_i + num_cells; i += 1 ) {
ghost_cells.push([row_i, i])
if ( i > 8 ) is_valid_hover = false
}
}
// Don't allow placing on existing ships
is_valid_hover = is_valid_hover &amp;&amp; !ghost_cells.some(([row_i, col_i]) => this.is_ship_cell(row_i, col_i))
if ( is_valid_hover ) {
this.ship_ghost_cells = ghost_cells
} else {
this.ship_ghost_cells = []
}
} else {
this.ship_ghost_cells = []
}
}
/**
* Returns true if the cell at [row_index, column_index] is a ship.
* @param {number} row_i
* @param {number} col_i
* @return {boolean}
*/
is_ship_cell(row_i, col_i) {
return this.rows[row_i] &amp;&amp; this.rows[row_i][col_i] &amp;&amp; isShipCell(this.rows[row_i][col_i].render)
}
/**
* Hide the ghost ship when the mouse leaves the grid.
*/
on_mouse_leave() {
this.ship_ghost_cells = []
}
/**
* Returns a truthy value if the given cell is a ghost ship.
* @param {number} row_i
* @param {number} col_i
* @return {boolean}
*/
is_ghost_cell(row_i, col_i) {
return !!this.ship_ghost_cells.find(([cell_row_i, cell_col_i]) => cell_row_i === row_i &amp;&amp; cell_col_i === col_i)
}
/**
* When keydown, check if shift was pressed. If so, update the placement.
* @param event
*/
on_keydown(event) {
if ( event.key === 'Shift' ) {
this.shift_pressed = true
if ( this.ship_ghost_cells.length > 0 ) {
this.on_cell_hover(this.ship_ghost_cells[0][0], this.ship_ghost_cells[0][1])
}
}
}
/**
* When keyup, check if shift was released. If so, update the placement.
* @param event
*/
on_keyup(event) {
if ( event.key === 'Shift' ) {
this.shift_pressed = false
if ( this.ship_ghost_cells.length > 0 ) {
this.on_cell_hover(this.ship_ghost_cells[0][0], this.ship_ghost_cells[0][1])
}
}
}
}
export default GameBoardComponent
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,111 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: components/GridCell.component.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: components/GridCell.component.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import {Component} from '../../lib/vues6.js'
import {GridCellState} from '../module/util.js'
const template = `
&lt;div
class="game-board-cell-component"
@click="on_click"
@mouseover="on_hover($event)"
@mouseleave="on_mouse_leave"
v-bind:class="{ disabled: render === GridCellState.Disabled, available: render === GridCellState.Available,
ship: render == GridCellState.Ship, damaged: render == GridCellState.Damaged, sunk: render == GridCellState.Sunk,
missed: render == GridCellState.Missed, ghost: has_ghost_ship }"
>
&lt;/div>
`
/**
* A component which represents a single, programmable grid cell.
* @extends Component
*/
class GridCellComponent extends Component {
static get selector() { return 'app-game-cell' }
static get template() { return template }
/** Properties that can be passed into this component. */
static get props() {
return [
'render',
'has_ghost_ship',
]
}
/** Make the "GridCellState" enum available in the template. */
GridCellState = GridCellState
/**
* Fire a click event.
*/
on_click() {
this.$emit('click')
}
/**
* Fire a hover event.
* @param $event
*/
on_hover($event) {
this.$emit('hover', $event)
}
/**
* Fire a "hoverchange" event.
*/
on_mouse_leave() {
this.$emit('hoverchange')
}
}
export default GridCellComponent
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,160 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: components/ScoreBoard.component.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: components/ScoreBoard.component.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import {Component} from '../../lib/vues6.js'
import game_service from '../services/GameState.service.js'
import {Player, GameState} from '../module/util.js'
const template = `
&lt;div class="app-scoreboard-component">
&lt;table class="scoreboard_table" style="width:50%">
&lt;tr class="scoreboard_rows">
&lt;th class="scoreboard_table_empty">&lt;/th>
&lt;th class="scoreboard_table_header" colspan="3">scoreboard&lt;/th>
&lt;/tr>
&lt;tr class="scoreboard_header_scoreProgress">
&lt;th class="scoreboard_table_empty">&lt;/th>
&lt;td class="scoreboard_data">score&lt;/td>
&lt;td class="scoreboard_data">progress&lt;/td>
&lt;/tr>
&lt;tr class="scoreboard_rows_score&amp;progress">
&lt;td class="scoreboard_player">{{ current_player === Player.One ? '➜ ' : '' }}Player 1{{ winning_player === Player.One ? ' ★' : '' }}&lt;/td>
&lt;td class="scoreboard_data">{{player_one_score}}&lt;/td>
&lt;td class="scoreboard_data">{{player_one_progress * 100}}%&lt;/td>
&lt;/tr>
&lt;tr class="scoreboard_lastRow">
&lt;td class="scoreboard_player">{{ current_player === Player.Two ? '➜ ' : '' }}Player 2{{ winning_player === Player.Two ? ' ★' : '' }}&lt;/td>
&lt;td class="scoreboard_data">{{player_two_score}}&lt;/td>
&lt;td class="scoreboard_data">{{player_two_progress * 100}}%&lt;/td>
&lt;/tr>
&lt;/table>
&lt;/div>
`
/**
* A component which represents the programmable scoreboard.
* @extends Component
*/
class ScoreBoardComponent extends Component {
static get selector() { return 'app-scoreboard' }
static get template() { return template }
static get props() { return [] }
/**
* The score of player one.
* @type {number}
*/
player_one_score = 0
/**
* The score of player two.
* @type {number}
*/
player_two_score = 0
/**
* The progress of player one, as a decimal.
* @type {number}
*/
player_one_progress = 0
/**
* The progress of player two, as a decimal.
* @type {number}
*/
player_two_progress = 0
/**
* The current player.
* @type {string|undefined}
*/
current_player = undefined
/**
* The winning player.
* @type {string|undefined}
*/
winning_player = undefined
Player = Player
/**
* Called when the component is initialized.
* @return {Promise&lt;void>}
*/
async vue_on_create() {
game_service.on_state_change(() => {
this.update()
})
this.update()
}
/**
* Fetch new data from the game service.
*/
update() {
// here is where you should fetch the data from the game service and update variables on the class
this.player_one_score = game_service.get_player_score(Player.One)
this.player_two_score = game_service.get_player_score(Player.Two)
this.player_one_progress = game_service.get_progress(Player.One)
this.player_two_progress = game_service.get_progress(Player.Two)
if ( game_service.get_game_state() !== GameState.PlayerVictory )
this.current_player = game_service.get_current_player()
else {
this.current_player = undefined
this.winning_player = game_service.get_current_player()
}
}
}
export default ScoreBoardComponent
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,283 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: components/TopLevel.component.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: components/TopLevel.component.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import {Component} from '../../lib/vues6.js'
import {GameState, ShipType} from '../module/util.js'
import {instructions} from '../module/lang.js'
import game_service from '../services/GameState.service.js'
import {GameSounds} from '../module/sounds.js'
const template = `
&lt;div class="top-level-container">
&lt;div class="top-level-component">
&lt;div v-if="current_state === GameState.ChoosingNumberOfShips" class="game-choose-ships-container">
&lt;span v-if="instructions">{{ instructions }}&lt;/span>
&lt;div style="margin-top: 30px;">
&lt;button @click="ship(1)" class="shipBtn">1 ship&lt;/button>
&lt;button @click="ship(2)" class="shipBtn">2 ships&lt;/button>
&lt;button @click="ship(3)" class="shipBtn">3 ships&lt;/button>
&lt;button @click="ship(4)" class="shipBtn">4 ships&lt;/button>
&lt;button @click="ship(5)" class="shipBtn">5 ships&lt;/button>
&lt;/div>
&lt;/div>
&lt;div v-if="current_state === GameState.PromptPlayerChange" class="game-player-change-container">
It is now {{ current_player_display }}'s turn!
&lt;button @click="confirm_player_change" class="playerBtn">Continue&lt;/button>
&lt;/div>
&lt;div
v-if="current_state !== GameState.ChoosingNumberOfShips &amp;&amp; current_state !== GameState.PromptPlayerChange &amp;&amp; instructions"
class="instructions"
>
{{ instructions.replace('{player}', current_player_display) }}
&lt;/div>
&lt;div
v-if="current_state !== GameState.ChoosingNumberOfShips
&amp;&amp; current_state !== GameState.PromptPlayerChange
&amp;&amp; current_state !== GameState.PlayerVictory"
class="game-boards-container"
>
&lt;!-- Opponent's board -->
&lt;div class="game-board">
&lt;app-game-board
v-bind:rows="opponent_rows"
v-bind:is_missile_mode="player_is_firing_missiles"
@missilefired="on_missile_fired"
>&lt;/app-game-board>
&lt;div class="fleet-label">Opposing fleet&lt;/div>
&lt;/div>
&lt;!-- Player's board -->
&lt;div class="game-board">
&lt;app-game-board
v-bind:rows="player_rows"
v-bind:is_placement_mode="player_is_placing_ships"
v-bind:ships_to_place="ships_to_place"
@shipplaced="on_ship_placed"
>&lt;/app-game-board>
&lt;div class="fleet-label">Your fleet&lt;/div>
&lt;/div>
&lt;/div>
&lt;div
v-if="current_state === GameState.PlayerVictory"
class="game-boards-container"
>
&lt;!-- Winner's board -->
&lt;div class="game-board">
&lt;app-game-board
v-bind:rows="player_rows"
>&lt;/app-game-board>
&lt;div class="fleet-label">{{ current_player_display }}'s fleet (winner)&lt;/div>
&lt;/div>
&lt;!-- Loser's board -->
&lt;div class="game-board">
&lt;app-game-board
v-bind:rows="opponent_rows"
>&lt;/app-game-board>
&lt;div class="fleet-label">{{ current_opponent_display }}'s fleet&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="scoreboard-container">
&lt;app-scoreboard>&lt;/app-scoreboard>
&lt;/div>
&lt;/div>
`
/**
* Top-level component which manages the display of the entire game.
* @extends Component
*/
class TopLevelComponent extends Component {
static get selector() { return 'app-top-level' }
static get template() { return template }
static get props() { return [] }
/**
* Make the game state accessible w/in the template.
* @type {object}
*/
GameState = GameState
/**
* The current game state.
* @type {GameState|undefined}
*/
current_state = undefined
/**
* The opponent's grid data.
* @type {object[][]}
*/
opponent_rows = []
/**
* The player's grid data.
* @type {object[][]}
*/
player_rows = []
/**
* The current instructions to be shown to the user.
* @type {string}
*/
instructions = instructions[GameState.ChoosingNumberOfShips]
/**
* True if the player should be able to place their ships.
* @type {boolean}
*/
player_is_placing_ships = false
/**
* True if the player should be able to fire missiles at their opponent.
* @type {boolean}
*/
player_is_firing_missiles = false
/**
* If in placement mode, the ships that are yet to be placed.
* @type {ShipType[]}
*/
ships_to_place = []
/**
* The display name of the current player.
* @type {string}
*/
current_player_display = ''
/**
* The display name of the current opponent.
* @type {string}
*/
current_opponent_display = ''
/**
* Called when the component is initialized.
* @return {Promise&lt;void>}
*/
async vue_on_create() {
this.current_state = game_service.get_game_state()
// Called every time the game state is updated
game_service.on_state_change((next_state, was_refresh) => {
this.current_state = next_state
this.opponent_rows = game_service.get_current_opponent_state()
this.player_rows = game_service.get_current_player_state()
this.current_player_display = game_service.get_player_display(game_service.get_current_player())
this.current_opponent_display = game_service.get_player_display(game_service.get_current_opponent())
this.player_is_placing_ships = next_state === GameState.PlayerSetup
this.player_is_firing_missiles = next_state === GameState.PlayerTurn
if ( !was_refresh &amp;&amp; this.player_is_placing_ships ) {
this.ships_to_place = game_service.get_possible_boats()
}
if ( next_state === GameState.PlayerVictory ) {
const [victor_state, loser_state] = game_service.get_player_victory_state()
this.player_rows = victor_state
this.opponent_rows = loser_state
}
this.instructions = instructions[this.current_state]
})
}
/**
* Set the number of boats.
* @param {number} n
*/
ship(n) {
game_service.set_n_boats(n)
game_service.advance_game_state()
}
/**
* Called when the current user has placed a ship.
*/
on_ship_placed() {
this.ships_to_place.shift()
if ( this.ships_to_place.length &lt; 1 ) {
// We've placed all the ships. Let's move on.
game_service.advance_game_state()
}
}
/**
* Called when the player attempts to fire a missile.
* @param {number} row_index
* @param {number} column_index
*/
async on_missile_fired([row_index, column_index]) {
if ( this.player_is_firing_missiles ) {
await GameSounds.Fire.play()
const success = game_service.attempt_missile_fire([row_index, column_index])
if ( success ) await GameSounds.Hit.play()
else await GameSounds.Miss.play()
// Give the user time to see whether they hit or not
setTimeout(() => {
game_service.advance_game_state()
}, 2000)
}
}
/**
* Called when the player has confirmed the player change.
*/
confirm_player_change() {
game_service.advance_game_state()
}
}
export default TopLevelComponent
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 116 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 118 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 114 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 117 KiB

@ -0,0 +1,116 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Home</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Home</h1>
<h3> </h3>
<section>
<article><h1>Battleship</h1>
<h2>EECS 448 - Project 1</h2>
<p>This is a basic battleship game created as our submission for project 1 for EECS 448 at the University of Kansas.</p>
<h2>Structure Info</h2>
<p>This project has been wired up to use Vue.js to help organize components of the game.</p>
<p>These components are defined in files that end in the <code>.component.js</code> extension, and are located in the <code>src/components/</code> directory.</p>
<p>The entry point for the project is the <code>index.html</code>. This file contains the basic logic for loading Vue, and adding the game board to the page.</p>
<p>Obviously, we'll flesh out the look-and-feel as we go along. This is just a basic starter for now.</p>
<h2>How to Run</h2>
<p>The easiest way to run this project is by creating a basic static web server using Python. This is super simple:</p>
<ol>
<li>Open a terminal or command prompt to the root of this project (i.e. the directory this file is in).</li>
<li>Start the server: <code>python -m http.server</code></li>
</ol>
<p>This will start a web server on port 8000. You can then run the game by navigating to http://localhost:8000/ from a web browser.</p>
<h2>Documentation</h2>
<p>You can <a href="https://htmlpreview.github.io/?https://raw.githubusercontent.com/EECS-448-Battleship/project-1/master/documentation/generated/index.html">preview it here</a>. Otherwise, it is generated by JSDoc in the <code>documentation/generated</code> directory.</p>
<h3>Re-generating the documentation</h3>
<p>To regenerate the docs, you need Node.js and the Yarn package manager installed. Then, just:</p>
<pre class="prettyprint source lang-shell"><code>cd documentation
./generate.sh
</code></pre>
<h2>Third-Party Libraries</h2>
<p>The files in the <code>lib/</code> are external libraries used in this project.</p>
<ul>
<li>Vue.js
<ul>
<li>A front-end framework. Used under the terms of the MIT license.</li>
<li>https://github.com/vuejs/vue</li>
</ul>
</li>
<li>VuES6.js
<ul>
<li>A kind-of crappy loader for defining Vue components using ES6 classes.</li>
<li>Also used under the terms of the MIT license.</li>
<li>https://code.garrettmills.dev/garrettmills/vues6</li>
</ul>
</li>
<li>Sound effects obtained from https://www.zapsplat.com and used with permission.</li>
</ul>
<h2>Contributors</h2>
<ul>
<li>Lucas Brakenridge</li>
<li>Javier Barea Lara</li>
<li>Garrett Mills</li>
<li>Evan Powell</li>
<li>Alec Horlick-Mills</li>
</ul></article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,183 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Class: InvalidAdvanceStateError</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Class: InvalidAdvanceStateError</h1>
<section>
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>
<span class="ancestors"><a href="module-errors.html">errors</a>.</span>InvalidAdvanceStateError<span class="signature">()</span><span class="type-signature"></span></h2>
<div class="class-description">Error thrown when the program tries to advance the state, but it is
invalid.</div>
</header>
<article>
<div class="container-overview">
<h2>Constructor</h2>
<h4 class="name" id="InvalidAdvanceStateError"><span class="type-signature"></span>new InvalidAdvanceStateError<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_errors.js.html">module/errors.js</a>, <a href="module_errors.js.html#line15">line 15</a>
</li></ul></dd>
</dl>
</div>
<h3 class="subsection-title">Extends</h3>
<ul>
<li>Error</li>
</ul>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Class: InvalidMissileFireAttemptError</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Class: InvalidMissileFireAttemptError</h1>
<section>
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>
<span class="ancestors"><a href="module-errors.html">errors</a>.</span>InvalidMissileFireAttemptError<span class="signature">()</span><span class="type-signature"></span></h2>
<div class="class-description">Error thrown when a missile is fired at an invalid cell.</div>
</header>
<article>
<div class="container-overview">
<h2>Constructor</h2>
<h4 class="name" id="InvalidMissileFireAttemptError"><span class="type-signature"></span>new InvalidMissileFireAttemptError<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_errors.js.html">module/errors.js</a>, <a href="module_errors.js.html#line21">line 21</a>
</li></ul></dd>
</dl>
</div>
<h3 class="subsection-title">Extends</h3>
<ul>
<li>Error</li>
</ul>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,183 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Class: InvalidShipPlacementError</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Class: InvalidShipPlacementError</h1>
<section>
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>
<span class="ancestors"><a href="module-errors.html">errors</a>.</span>InvalidShipPlacementError<span class="signature">()</span><span class="type-signature"></span></h2>
<div class="class-description">Placeholder class for an error that is thrown when a ship is placed
in an invalid position.</div>
</header>
<article>
<div class="container-overview">
<h2>Constructor</h2>
<h4 class="name" id="InvalidShipPlacementError"><span class="type-signature"></span>new InvalidShipPlacementError<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_errors.js.html">module/errors.js</a>, <a href="module_errors.js.html#line8">line 8</a>
</li></ul></dd>
</dl>
</div>
<h3 class="subsection-title">Extends</h3>
<ul>
<li>Error</li>
</ul>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Module: errors</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Module: errors</h1>
<section>
<header>
</header>
<article>
<div class="container-overview">
</div>
<h3 class="subsection-title">Classes</h3>
<dl>
<dt><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></dt>
<dd></dd>
<dt><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></dt>
<dd></dd>
<dt><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></dt>
<dd></dd>
</dl>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,161 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Module: lang</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Module: lang</h1>
<section>
<header>
</header>
<article>
<div class="container-overview">
</div>
<h3 class="subsection-title">Members</h3>
<h4 class="name" id="~instructions"><span class="type-signature">(inner, constant) </span>instructions<span class="type-signature"> :object</span></h4>
<div class="description">
Enum of all possible instructions.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">object</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_lang.js.html">module/lang.js</a>, <a href="module_lang.js.html#line9">line 9</a>
</li></ul></dd>
</dl>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,480 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Class: Sound</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Class: Sound</h1>
<section>
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>
<span class="ancestors"><a href="module-sounds.html">sounds</a>~</span>Sound<span class="signature">(src)</span><span class="type-signature"></span></h2>
<div class="class-description">A thin wrapper for sound effects.</div>
</header>
<article>
<div class="container-overview">
<h2>Constructor</h2>
<h4 class="name" id="Sound"><span class="type-signature"></span>new Sound<span class="signature">(src)</span><span class="type-signature"></span></h4>
<div class="description">
Construct the sound.
</div>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>src</code></td>
<td class="type">
<span class="param-type">string</span>
</td>
<td class="description last">URL of the file</td>
</tr>
</tbody>
</table>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_sounds.js.html">module/sounds.js</a>, <a href="module_sounds.js.html#line13">line 13</a>
</li></ul></dd>
</dl>
</div>
<h3 class="subsection-title">Members</h3>
<h4 class="name" id="sound"><span class="type-signature"></span>sound<span class="type-signature"> :HTMLAudioElement</span></h4>
<div class="description">
The sound element.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">HTMLAudioElement</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_sounds.js.html">module/sounds.js</a>, <a href="module_sounds.js.html#line18">line 18</a>
</li></ul></dd>
</dl>
<h3 class="subsection-title">Methods</h3>
<h4 class="name" id="play"><span class="type-signature">(async) </span>play<span class="signature">()</span><span class="type-signature"></span></h4>
<div class="description">
Start playing the sound.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_sounds.js.html">module/sounds.js</a>, <a href="module_sounds.js.html#line30">line 30</a>
</li></ul></dd>
</dl>
<h4 class="name" id="stop"><span class="type-signature"></span>stop<span class="signature">()</span><span class="type-signature"></span></h4>
<div class="description">
Pause the sound.
</div>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_sounds.js.html">module/sounds.js</a>, <a href="module_sounds.js.html#line42">line 42</a>
</li></ul></dd>
</dl>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,168 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Module: sounds</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Module: sounds</h1>
<section>
<header>
</header>
<article>
<div class="container-overview">
</div>
<h3 class="subsection-title">Classes</h3>
<dl>
<dt><a href="module-sounds-Sound.html">Sound</a></dt>
<dd></dd>
</dl>
<h3 class="subsection-title">Members</h3>
<h4 class="name" id="~GameSounds"><span class="type-signature">(inner, constant) </span>GameSounds<span class="type-signature"> :object</span></h4>
<div class="description">
Enum of the various sound effects available in the game.
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">object</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="module_sounds.js.html">module/sounds.js</a>, <a href="module_sounds.js.html#line51">line 51</a>
</li></ul></dd>
</dl>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: module/errors.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: module/errors.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/** @module errors */
/**
* Placeholder class for an error that is thrown when a ship is placed
* in an invalid position.
* @extends Error
*/
export class InvalidShipPlacementError extends Error {}
/**
* Error thrown when the program tries to advance the state, but it is
* invalid.
* @extends Error
*/
export class InvalidAdvanceStateError extends Error {}
/**
* Error thrown when a missile is fired at an invalid cell.
* @extends Error
*/
export class InvalidMissileFireAttemptError extends Error {}
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: module/lang.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: module/lang.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/** @module lang **/
import { GameState } from './util.js'
/**
* Enum of all possible instructions.
* @type {object}
*/
const instructions = {
[GameState.ChoosingNumberOfShips]: 'Select the number of ships both players will play with.',
[GameState.PlayerSetup]: 'Place your ships on your fleet\'s grid. You can hold the shift key to change the orientation.',
[GameState.PlayerTurn]: 'Select a cell on the opposing fleet\'s grid to fire a missile.',
[GameState.PlayerVictory]: '{player} won!'
}
export { instructions }
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: module/sounds.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: module/sounds.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/** @module sounds */
import { appUrl } from './util.js'
/**
* A thin wrapper for sound effects.
*/
class Sound {
/**
* Construct the sound.
* @param {string} src - URL of the file
*/
constructor(src) {
/**
* The sound element.
* @type {HTMLAudioElement}
*/
this.sound = document.createElement('audio')
this.sound.src = src
this.sound.setAttribute('preload', 'auto')
this.sound.setAttribute('controls', 'none')
this.sound.style.display = 'none'
document.body.appendChild(this.sound)
}
/**
* Start playing the sound.
*/
async play() {
const duration = this.sound.duration
await this.sound.play()
await new Promise(res => {
setTimeout(res, duration * 1000)
})
}
/**
* Pause the sound.
*/
stop() {
this.sound.pause()
}
}
/**
* Enum of the various sound effects available in the game.
* @type {object}
*/
const GameSounds = {
Victory: new Sound(appUrl('/lib/sounds/cartoon_success_fanfair.mp3')),
Fire: new Sound(appUrl('/lib/sounds/zapsplat_warfare_mortar_projectile_launch_002_25232.mp3')),
Hit: new Sound(appUrl('/lib/sounds/zapsplat_warfare_bomb_whizz_in_hit_close_by_explosion_med_003_48060.mp3')),
Miss: new Sound(appUrl('/lib/sounds/zapsplat_nature_water_pour_medium_amount_deep_sudden_fast_002_52765.mp3')),
}
export { GameSounds }
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,177 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: module/util.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: module/util.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/** @module util */
/**
* Enum of all possible states of a grid cell.
* @type {object}
*/
export const GridCellState = {
// Empty cell, default state
Available: 'available',
// Disabled. Ship cannot be placed here.
Disabled: 'disabled',
// There is a ship in this cell.
Ship: 'ship',
// This cell contains part of a ship which was damaged but not sunk
Damaged: 'damaged',
// This cell contains part of a ship which was sunk
Sunk: 'sunk',
// This cell was targeted, but nothing was hit
Missed: 'missed',
}
/**
* Returns true if the given grid cell state represents a ship in some way.
* @param {GridCellState} grid_cell_state
* @return {boolean}
*/
export function isShipCell(grid_cell_state) {
return [
GridCellState.Ship,
GridCellState.Damaged,
GridCellState.Sunk,
].includes(grid_cell_state)
}
/**
* Returns true if the given grid cell state can be fired upon.
* @param {GridCellState} grid_cell_state
* @return {boolean}
*/
export function isValidTargetCell(grid_cell_state) {
return [
GridCellState.Ship,
GridCellState.Available,
].includes(grid_cell_state)
}
/**
* Enum of all possible players.
* @type {object}
*/
export const Player = {
One: 'player_one',
Two: 'player_two',
}
/**
* Enum of all possible game states. These are player-agnostic.
* @type {object}
*/
export const GameState = {
// Both players are choosing the number of ships to play with (1-5)
ChoosingNumberOfShips: 'choosing_number_of_ships',
// A player is placing their ships
PlayerSetup: 'player_setup',
// We are prompting to change to the other player
PromptPlayerChange: 'prompt_player_change',
// It is the player's turn to fire a missle at their opponent
PlayerTurn: 'player_turn',
// A player has won
PlayerVictory: 'player_victory',
}
/**
* The various supported ship types, by dimension.
* @type {object}
*/
export const ShipType = {
x1: '1x1',
x2: '1x2',
x3: '1x3',
x4: '1x4',
x5: '1x5',
}
export function isShipType(type) {
return ['1x1', '1x2', '1x3', '1x4', '1x5'].includes(type)
}
/**
* Makes a deep copy of the value passed in.
* @param {*} obj
* @return {*}
*/
export function clone(obj) {
// If it's just a value, return it.
if ( typeof obj !== 'object' || obj === null ) return obj
// If it's an array, copy its values.
if ( Array.isArray(obj) ) return obj.map(x => clone(x))
// If it's an object, copy its properties.
const copy = {}
for ( const prop in obj ) {
copy[prop] = clone(obj[prop])
}
return copy
}
/**
* Generate an absolute URL to a file w/in the project directory.
* @param {string} path
* @return {string}
*/
export function appUrl(path) {
if ( path.startsWith('/') ) path = path.slice(1)
return `${APP_BASE_PATH}${path}`
}
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,25 @@
/*global document */
(() => {
const source = document.getElementsByClassName('prettyprint source linenums');
let i = 0;
let lineNumber = 0;
let lineId;
let lines;
let totalLines;
let anchorHash;
if (source && source[0]) {
anchorHash = document.location.hash.substring(1);
lines = source[0].getElementsByTagName('li');
totalLines = lines.length;
for (; i < totalLines; i++) {
lineNumber++;
lineId = `line${lineNumber}`;
lines[i].id = lineId;
if (lineId === anchorHash) {
lines[i].className += ' selected';
}
}
}
})();

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -0,0 +1,2 @@
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);

@ -0,0 +1,28 @@
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();

@ -0,0 +1,760 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: services/GameState.service.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: services/GameState.service.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import { Player, GridCellState, GameState, clone, ShipType, isShipType, isShipCell, isValidTargetCell } from '../module/util.js'
import { InvalidShipPlacementError, InvalidAdvanceStateError, InvalidMissileFireAttemptError } from '../module/errors.js'
/**
* Singleton service for managing the state of the game.
*/
export class GameStateService {
/**
* A mapping of player => game board state.
* @private
* @type {object}
*/
player_x_game_board = {}
/**
* A mapping of player => ship definitions.
* @private
* @type {object}
*/
player_x_ships = {}
/**
* An array of all players. This is mostly for internal purposes.
* @private
* @type {string[]}
*/
players = [Player.One, Player.Two]
/**
* Number of rows in the grid.
* @private
* @type {number}
*/
n_rows = 9
/**
* Number of cols in the grid.
* @private
* @type {number}
*/
n_cols = 9
/**
* Number boats placed on the board.
* @private
* @type {number}
*/
n_boats = 1
/**
* gets the number of boats placed on the board
* @private
* @return {number}
*/
get_n_boats(){
return (this.n_boats);
}
/**
* sets the number of boats to a valid number
* @private
* @return none
*/
set_n_boats(number){
if(number >= 1 &amp;&amp; number &lt;= 5 )
{
this.n_boats = number;
}
}
/**
* Given the number of boats set by the player (n_boats), return an array
* of possible ShipTypes.
* @return {string[]}
*/
get_possible_boats(){
if (this.get_n_boats() === 1) {
return [ShipType.x1]
}
else if (this.get_n_boats() === 2) {
return [ShipType.x1, ShipType.x2]
}
else if (this.get_n_boats() === 3) {
return [ShipType.x1, ShipType.x2, ShipType.x3]
}
else if (this.get_n_boats() === 4) {
return [ShipType.x1, ShipType.x2, ShipType.x3, ShipType.x4]
}
else if (this.get_n_boats() === 5) {
return [ShipType.x1, ShipType.x2, ShipType.x3, ShipType.x4, ShipType.x5]
}
}
/**
* The current state of the game.
* @private
* @type {string}
*/
current_state = GameState.ChoosingNumberOfShips
/**
* The current player.
* @private
* @type {string}
*/
current_player = Player.One
/**
* The current opponent.
* @private
* @type {string}
*/
current_opponent = Player.Two
/**
* If the current state is the PromptPlayerChange, then this is
* the state that we should move to next.
* @type {undefined|string}
*/
post_player_change_state = undefined
/**
* True if, during the current turn, the user has tried to fire a missile.
* @type {boolean}
*/
current_turn_had_missile_attempt = false
/**
* Array of functions that are called when the game state changes.
* @type {function[]}
*/
game_state_change_listeners = []
/**
* Construct a new game service. Initialize any internal states.
*/
constructor() {
// Generate empty boards for each player
for ( const player of this.players ) {
this.player_x_game_board[player] = this._build_empty_board()
this.player_x_ships[player] = []
}
}
/**
* Get the dimensions of the board as [rows, cols].
* @example const [n_rows, n_cols] = game_service.get_dimensions()
* @return {number[]}
*/
get_dimensions() {
return [this.n_rows, this.n_cols]
}
/**
* Get the player who is the focus of the current game state.
* @return {string}
*/
get_current_player() {
return this.current_player
}
/**
* Get the player who is NOT the focus of the current game state.
* @return {string}
*/
get_current_opponent() {
return this.current_opponent
}
/**
* Get the state of the current player's board, as it should appear to them.
* @return {object[][]}
*/
get_current_player_state() {
// The player can see everything about their own board, so just return it.
// Return a deep-copy, so internal state can't be modified.
return clone(this.player_x_game_board[this.current_player])
}
/**
* Get the state of the current opponent's board, as it should appear to the
* current player. Note that the current player cannot see "ship" spaces, only
* available, damaged, missed, or sunk.
* @return {object[][]}
*/
get_current_opponent_state() {
// Return a deep-copy, so internal state can't be modified.
const state = clone(this.player_x_game_board[this.current_opponent])
const hidden_states = [
GridCellState.Disabled,
GridCellState.Ship,
]
return state.map(row => {
return row.map(cell => {
if ( hidden_states.includes(cell.render) ) {
// This is a hidden state, so hide it
cell.render = GridCellState.Available
}
return cell
})
})
}
/**
* Get the states that should be shown on the victory screen.
* First element is the winner's state, second element is the loser's state.
* @return {object[][][]}
*/
get_player_victory_state(){
return [clone(this.player_x_game_board[this.current_player]), clone(this.player_x_game_board[this.current_opponent])]
}
/**
* get the "score" (the number of hits) that the
* current player has (counting sunk ships)
* @return {number}
* @private
*/
get_player_score(player) {
let score = 0
this.player_x_game_board[player].some(row => {
row.some(cell => {
if ( cell.render === GridCellState.Damaged || cell.render === GridCellState.Sunk ) {
score += 1
}
})
})
return score
}
/**
* gets the number of the boats (sunken, damaged or not) that the opponent has
* used to help keep get progress method looking clean
* @return {number}
* @private
*/
get_boat_count(player){
let boat_count = 0
this.player_x_game_board[player].some(row => {
row.some(cell => {
if ( isShipCell(cell.render) ) {
boat_count += 1
}
})
})
return boat_count
}
/**
* gets the progress (hits/total boats) that the player has
* @return {number}
* @private
*/
get_progress(player){
const boat_count = this.get_boat_count(player)
if ( boat_count !== 0 ) {
return (this.get_player_score(player) / boat_count).toFixed(2)
} else {
return 0
}
}
/**
* Get the current game state.
* @return {string}
*/
get_game_state() {
return clone(this.current_state)
}
/**
* responsible for advancing the game state
* will be consisting of
*/
advance_game_state() {
/** functions to be made that validate:
* 1) number of ships
* 2) player one placement
* 3) player two placement
* 4) player one turn
* 5) advance to player 2
* 6) player 2 turn
* 7) advance to player one
* 8) player win
*/
if (this.current_state === GameState.ChoosingNumberOfShips) {
if (this.n_boats >= 1 &amp;&amp; this.n_boats &lt;= 5) {
this.current_state = GameState.PromptPlayerChange;
this.post_player_change_state = GameState.PlayerSetup;
this.current_player = Player.One;
this.current_opponent = Player.Two;
} else {
throw new InvalidAdvanceStateError("Invalid Number of Boats");
}
}
else if (this.current_state === GameState.PlayerSetup) {
if (this.current_player === Player.One) {
// because the place_ship handles all the validation
// all you need to do is make sure they have placed all the appropriate ships
if ( this.get_ship_entities(this.current_player).length === this.n_boats ) {
this.current_state = GameState.PromptPlayerChange;
this.post_player_change_state = GameState.PlayerSetup;
this.current_player = Player.Two;
this.current_opponent = Player.One;
}
else{
throw new InvalidAdvanceStateError("Player One has a problem with the number of boats selected");
}
}
else if (this.current_player === Player.Two) {
if ( this.get_ship_entities(this.current_player).length === this.n_boats ) {
this.current_state = GameState.PromptPlayerChange;
this.post_player_change_state = GameState.PlayerTurn;
this.current_player = Player.One;
this.current_opponent = Player.Two;
}
else{
throw new InvalidAdvanceStateError("Player Two has a problem with the number of boats selected");
}
}
}
else if (this.current_state === GameState.PlayerTurn &amp;&amp; this.current_player === Player.One) {
if (this.current_turn_had_missile_attempt === true) {
this.current_state = GameState.PromptPlayerChange;
this.post_player_change_state = GameState.PlayerTurn;
this.current_player = Player.Two;
this.current_opponent = Player.One;
}
else {
throw new InvalidAdvanceStateError("the player has not fired a missle");
}
}
else if (this.current_state === GameState.PlayerTurn &amp;&amp; this.current_player === Player.Two) {
if (this.current_turn_had_missile_attempt === true) {
this.current_state = GameState.PromptPlayerChange;
this.post_player_change_state = GameState.PlayerTurn;
this.current_player = Player.One;
this.current_opponent = Player.Two;
}
else {
throw new InvalidAdvanceStateError("the player has not fired a missle");
}
}
else if ( this.current_state === GameState.PromptPlayerChange ) {
if ( !this.post_player_change_state ) {
throw new InvalidAdvanceStateError('No state to advance to after player change!')
}
this.current_state = this.post_player_change_state
this.post_player_change_state = undefined
}
let winner = this.get_winner();
if(winner) {
this.current_state = GameState.PlayerVictory;
this.current_player = winner;
this.current_opponent = this.get_other_player(winner);
}
this.current_turn_had_missile_attempt = false
this.game_state_change_listeners.forEach(fn => fn(this.current_state, false))
}
/**
* Register a handler to be called when the game state changes.
* @param {function} handler
*/
on_state_change(handler) {
this.game_state_change_listeners.push(handler)
}
/**
* Attempt to fire a missile at the current opponent at the given coordinates.
* The coordinates should be an array of [row_index, column_index] where the missile should fire.
* Returns true if the missile hit an undamaged cell of a ship.
*
* @example
* If I want to fire a missile at row 5 column 7, then:
* game_service.attempt_missile_fire([5, 7])
*
* @param {number[]} coords
* @return {boolean}
*/
attempt_missile_fire([target_row_i, target_col_i]) {
if ( this.current_turn_had_missile_attempt ) {
throw new InvalidMissileFireAttemptError('Cannot fire more than once per turn.')
} else {
this.current_turn_had_missile_attempt = true
}
const target_cell = this._get_cell_state(this.current_opponent, target_row_i, target_col_i)
if ( !isValidTargetCell(target_cell.render) ) {
this.current_turn_had_missile_attempt = false
throw new InvalidMissileFireAttemptError('Cannot fire on cell with state: ' + target_cell.render)
}
if ( target_cell.render === GridCellState.Ship ) {
// We hit an un-hit ship cell!
this._set_cell_state(this.current_opponent, target_row_i, target_col_i, GridCellState.Damaged)
// set ships to sunk where appropriate
this._sink_damaged_ships(this.current_opponent)
this._trigger_view_update()
return true
} else if ( target_cell.render === GridCellState.Available ) {
// We missed...
this._set_cell_state(this.current_opponent, target_row_i, target_col_i, GridCellState.Missed)
}
this._trigger_view_update()
return false
}
/**
* Checks the player's ships. If any are fully damaged, it flags that ship's cells
* as "sunk" rather than damaged.
* @param {string} player
* @private
*/
_sink_damaged_ships(player) {
this.get_ship_entities(player).some(ship => {
const covered_cells = this.get_covered_cells(ship.coords_one, ship.coords_two)
const all_damaged = covered_cells.every(([cell_row, cell_col]) => {
return this._get_cell_state(player, cell_row, cell_col).render === GridCellState.Damaged
})
if ( all_damaged ) {
// The entire boat was damaged, so sink it
covered_cells.some(([cell_row, cell_col]) => {
this._set_cell_state(player, cell_row, cell_col, GridCellState.Sunk)
})
}
})
}
/**
* Attempt to place a ship of the given type at the given coordinates.
* Throws an InvalidShipPlacementError if the coordinates are invalid.
* Coordinates should be [row_index, column_index] of either end of the ship.
*
* @example
* If I am placing a 1x3 ship and I want it to be in row 3 column 2 horizontal
* to row 3 column 4, then I would call:
* game_service.place_ship(ShipType.x3, [3,2], [3,4])
*
* @param {string} ship_type
* @param {number[]} coords_one
* @param {number[]} coords_two
*/
place_ship(ship_type, coords_one, coords_two) {
// make sure the coordinates are valid for the given ship type
this.validate_coordinates(ship_type, coords_one, coords_two)
// get the ships for the current player
const player_ships = this.get_ship_entities(this.current_player)
// make sure they don't already have this ship type
const have_ship_type = player_ships.some(ship => ship.ship_type === ship_type)
if ( have_ship_type )
throw new InvalidShipPlacementError('A ship with this type has already been placed.')
// make sure they don't already have too many
if ( player_ships.length >= this.n_boats )
throw new InvalidShipPlacementError('This player cannot place any more ships.')
// place the ship
this.player_x_ships[this.current_player].push({ ship_type, coords_one, coords_two })
// mark the cells as having a ship in them
this.get_covered_cells(coords_one, coords_two).some(([row_i, col_i]) => {
this._set_cell_state(this.current_player, row_i, col_i, GridCellState.Ship)
})
// refresh the view
this._trigger_view_update()
}
/**
* Get an array of cell coordinates that are covered by a ship that spans
* the given coordinates.
*
* @example
* If a ship goes from row 1 column 1 to row 4 column 1, then I can get
* the coordinates of all cells covered by that ship using:
* game_service.get_covered_cells([1,1], [4,1])
* Which would return [[1,1], [2,1], [3,1], [4,1]].
*
* @param {number[]} coords_one
* @param {number[]} coords_two
* @return {number[][]}
*/
get_covered_cells(coords_one, coords_two) {
const [row_one, col_one] = coords_one
const [row_two, col_two] = coords_two
const [left_col, right_col] = [Math.min(col_one, col_two), Math.max(col_one, col_two)]
const [top_row, bottom_row] = [Math.min(row_one, row_two), Math.max(row_one, row_two)]
const is_horizontal = top_row === bottom_row
if ( is_horizontal ) {
return Array((right_col - left_col) + 1).fill('').map((_, i) => {
return [top_row, i + left_col]
})
} else {
return Array((bottom_row - top_row) + 1).fill('').map((_, i) => {
return [i + top_row, left_col]
})
}
}
/**
* Validate the given coordinates for the given ship type.
* Throws an InvalidShipPlacementError if the coordinates are invalid.
* Coordinates should be [row_index, column_index] of either end of the ship.
*
* @example
* If I am placing a 1x3 ship and I want it to be in row 3 column 2 horizontal
* to row 3 column 4, then I would call:
* game_service.validate_coordinates(ShipType.x3, [3,2], [3,4])
*
* @param {string} ship_type
* @param {number[]} coords_one
* @param {number[]} coords_two
*/
validate_coordinates(ship_type, coords_one, coords_two) {
if ( !isShipType(ship_type) ) throw new InvalidShipPlacementError('Invalid ship type: '+ship_type)
const ship_length = this.get_ship_length(ship_type)
const [row_one, col_one] = coords_one
const [row_two, col_two] = coords_two
const [left_col, right_col] = [Math.min(col_one, col_two), Math.max(col_one, col_two)]
const [top_row, bottom_row] = [Math.min(row_one, row_two), Math.max(row_one, row_two)]
const is_horizontal = top_row === bottom_row
const ship_cells = this.get_ship_cells(this.current_player)
const placement_cells = []
if ( is_horizontal ) {
// Make sure the input length matches the given ship type
if ( (right_col - left_col) !== (ship_length - 1) )
throw new InvalidShipPlacementError('Invalid coordinates: ship length is invalid')
Array(ship_length).fill('').map((_, i) => {
placement_cells.push([top_row, i + left_col])
})
} else {
// Make sure the input length matches the given ship type
if ( (bottom_row - top_row) !== (ship_length - 1) )
throw new InvalidShipPlacementError('Invalid coordinates: ship length is invalid')
Array(ship_length).fill('').map((_, i) => {
placement_cells.push([i + top_row, left_col])
})
}
// Make sure none of the placement cells overlap with existing ships
const has_overlap = ship_cells.some(([ship_row, ship_col]) => {
return placement_cells.some(([placement_row, placement_col]) => {
return ship_row === placement_row &amp;&amp; ship_col === placement_col
})
})
if ( has_overlap )
throw new InvalidShipPlacementError('Invalid coordinates: ship overlaps with others')
}
/**
* Get the number of cells the given ship type should occupy.
* @param {string} ship_type
* @return {number}
*/
get_ship_length(ship_type) {
if ( ship_type === ShipType.x1 ) return 1
if ( ship_type === ShipType.x2 ) return 2
if ( ship_type === ShipType.x3 ) return 3
if ( ship_type === ShipType.x4 ) return 4
if ( ship_type === ShipType.x5 ) return 5
}
/**
* Get the coordinates of all cells that have ships in them, for the given player.
* @param {string} player
* @return {number[]}
*/
get_ship_cells(player) {
const cells = []
this.player_x_game_board[player].some((row, row_i) => {
row.some((col, col_i) => {
if ( isShipCell(col.render) ) {
cells.push([row_i, col_i])
}
})
})
return cells
}
/**
* If there is a winner, this will return the Player that won.
* If no winner has been decided yet, will return undefined.
* @return {string|undefined}
*/
get_winner() {
const [player_1, player_2] = this.players
// Make sure to sink any fully-damaged ships
this._sink_damaged_ships(player_1)
const player_1_ship_cells = this.get_ship_cells(player_1)
const player_1_loses = (
(player_1_ship_cells.length > 0)
&amp;&amp; player_1_ship_cells.every(cell => this._get_cell_state(player_1, cell[0], cell[1]).render === GridCellState.Sunk)
)
if ( player_1_loses ) return player_2
// Make sure to sink any fully-damaged ships
this._sink_damaged_ships(player_2)
const player_2_ship_cells = this.get_ship_cells(player_2)
const player_2_loses = (
(player_2_ship_cells.length > 0)
&amp;&amp; player_2_ship_cells.every(cell => this._get_cell_state(player_2, cell[0], cell[1]).render === GridCellState.Sunk)
)
if ( player_2_loses ) return player_2
}
/**
* Returns the other player.
* @param {string} player
* @return {string}
*/
get_other_player(player) {
if ( player === Player.One ) return Player.Two
else if ( player === Player.Two ) return Player.One
}
/**
* Given a Player type, return the display value of that player.
* @param {string} player
* @return {string}
*/
get_player_display(player) {
if ( player === Player.One ) return 'Player 1'
else if ( player === Player.Two ) return 'Player 2'
}
/**
* Get an array of ship entities for the given player.
* @param {string} player
* @return {object[]}
*/
get_ship_entities(player) {
return clone(this.player_x_ships[player])
}
/**
* Build an empty structure of grid cells.
* @return {object[][]}
* @private
*/
_build_empty_board() {
return Array(this.n_rows).fill('').map(_ => {
return Array(this.n_cols).fill('').map(_ => {
return {
render: GridCellState.Available,
}
})
})
}
/**
* Set the state of the cell at the given coordinates on the player's board
* to the specified state.
* @param {string} player
* @param {number} row_i
* @param {number} col_i
* @param {string} state
* @private
*/
_set_cell_state(player, row_i, col_i, state) {
this.player_x_game_board[player][row_i][col_i].render = state
}
/**
* Get the state of the cell at the given coordinates on the player's board.
* @param {string} player
* @param {number} row_i
* @param {number} col_i
* @return {object}
* @private
*/
_get_cell_state(player, row_i, col_i) {
return this.player_x_game_board[player][row_i][col_i]
}
/**
* Force a view update without changing the current state.
* @private
*/
_trigger_view_update() {
this.game_state_change_listeners.forEach(fn => fn(this.current_state, true))
}
}
// Export a single instance, so it can be shared by all files
// To use the game state service, you should do:
// import game_service from './services/GameState.service.js'
const game_service = new GameStateService()
export default game_service
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-errors.html">errors</a></li><li><a href="module-lang.html">lang</a></li><li><a href="module-sounds.html">sounds</a></li><li><a href="module-util.html">util</a></li></ul><h3>Classes</h3><ul><li><a href="GameBoardComponent.html">GameBoardComponent</a></li><li><a href="GameStateService.html">GameStateService</a></li><li><a href="GridCellComponent.html">GridCellComponent</a></li><li><a href="module-errors.InvalidAdvanceStateError.html">InvalidAdvanceStateError</a></li><li><a href="module-errors.InvalidMissileFireAttemptError.html">InvalidMissileFireAttemptError</a></li><li><a href="module-errors.InvalidShipPlacementError.html">InvalidShipPlacementError</a></li><li><a href="module-sounds-Sound.html">Sound</a></li><li><a href="ScoreBoardComponent.html">ScoreBoardComponent</a></li><li><a href="TopLevelComponent.html">TopLevelComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.5</a> on Sat Sep 12 2020 16:40:09 GMT-0500 (Central Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

@ -0,0 +1,358 @@
@font-face {
font-family: 'Open Sans';
font-weight: normal;
font-style: normal;
src: url('../fonts/OpenSans-Regular-webfont.eot');
src:
local('Open Sans'),
local('OpenSans'),
url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg');
}
@font-face {
font-family: 'Open Sans Light';
font-weight: normal;
font-style: normal;
src: url('../fonts/OpenSans-Light-webfont.eot');
src:
local('Open Sans Light'),
local('OpenSans Light'),
url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/OpenSans-Light-webfont.woff') format('woff'),
url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg');
}
html
{
overflow: auto;
background-color: #fff;
font-size: 14px;
}
body
{
font-family: 'Open Sans', sans-serif;
line-height: 1.5;
color: #4d4e53;
background-color: white;
}
a, a:visited, a:active {
color: #0095dd;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
header
{
display: block;
padding: 0px 4px;
}
tt, code, kbd, samp {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
}
.class-description {
font-size: 130%;
line-height: 140%;
margin-bottom: 1em;
margin-top: 1em;
}
.class-description:empty {
margin: 0;
}
#main {
float: left;
width: 70%;
}
article dl {
margin-bottom: 40px;
}
article img {
max-width: 100%;
}
section
{
display: block;
background-color: #fff;
padding: 12px 24px;
border-bottom: 1px solid #ccc;
margin-right: 30px;
}
.variation {
display: none;
}
.signature-attributes {
font-size: 60%;
color: #aaa;
font-style: italic;
font-weight: lighter;
}
nav
{
display: block;
float: right;
margin-top: 28px;
width: 30%;
box-sizing: border-box;
border-left: 1px solid #ccc;
padding-left: 16px;
}
nav ul {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif;
font-size: 100%;
line-height: 17px;
padding: 0;
margin: 0;
list-style-type: none;
}
nav ul a, nav ul a:visited, nav ul a:active {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
line-height: 18px;
color: #4D4E53;
}
nav h3 {
margin-top: 12px;
}
nav li {
margin-top: 6px;
}
footer {
display: block;
padding: 6px;
margin-top: 12px;
font-style: italic;
font-size: 90%;
}
h1, h2, h3, h4 {
font-weight: 200;
margin: 0;
}
h1
{
font-family: 'Open Sans Light', sans-serif;
font-size: 48px;
letter-spacing: -2px;
margin: 12px 24px 20px;
}
h2, h3.subsection-title
{
font-size: 30px;
font-weight: 700;
letter-spacing: -1px;
margin-bottom: 12px;
}
h3
{
font-size: 24px;
letter-spacing: -0.5px;
margin-bottom: 12px;
}
h4
{
font-size: 18px;
letter-spacing: -0.33px;
margin-bottom: 12px;
color: #4d4e53;
}
h5, .container-overview .subsection-title
{
font-size: 120%;
font-weight: bold;
letter-spacing: -0.01em;
margin: 8px 0 3px 0;
}
h6
{
font-size: 100%;
letter-spacing: -0.01em;
margin: 6px 0 3px 0;
font-style: italic;
}
table
{
border-spacing: 0;
border: 0;
border-collapse: collapse;
}
td, th
{
border: 1px solid #ddd;
margin: 0px;
text-align: left;
vertical-align: top;
padding: 4px 6px;
display: table-cell;
}
thead tr
{
background-color: #ddd;
font-weight: bold;
}
th { border-right: 1px solid #aaa; }
tr > th:last-child { border-right: 1px solid #ddd; }
.ancestors, .attribs { color: #999; }
.ancestors a, .attribs a
{
color: #999 !important;
text-decoration: none;
}
.clear
{
clear: both;
}
.important
{
font-weight: bold;
color: #950B02;
}
.yes-def {
text-indent: -1000px;
}
.type-signature {
color: #aaa;
}
.name, .signature {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
}
.details { margin-top: 14px; border-left: 2px solid #DDD; }
.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; }
.details dd { margin-left: 70px; }
.details ul { margin: 0; }
.details ul { list-style-type: none; }
.details li { margin-left: 30px; padding-top: 6px; }
.details pre.prettyprint { margin: 0 }
.details .object-value { padding-top: 0; }
.description {
margin-bottom: 1em;
margin-top: 1em;
}
.code-caption
{
font-style: italic;
font-size: 107%;
margin: 0;
}
.source
{
border: 1px solid #ddd;
width: 80%;
overflow: auto;
}
.prettyprint.source {
width: inherit;
}
.source code
{
font-size: 100%;
line-height: 18px;
display: block;
padding: 4px 12px;
margin: 0;
background-color: #fff;
color: #4D4E53;
}
.prettyprint code span.line
{
display: inline-block;
}
.prettyprint.linenums
{
padding-left: 70px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.prettyprint.linenums ol
{
padding-left: 0;
}
.prettyprint.linenums li
{
border-left: 3px #ddd solid;
}
.prettyprint.linenums li.selected,
.prettyprint.linenums li.selected *
{
background-color: lightyellow;
}
.prettyprint.linenums li *
{
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.params .name, .props .name, .name code {
color: #4D4E53;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 100%;
}
.params td.description > p:first-child,
.props td.description > p:first-child
{
margin-top: 0;
padding-top: 0;
}
.params td.description > p:last-child,
.props td.description > p:last-child
{
margin-bottom: 0;
padding-bottom: 0;
}
.disabled {
color: #454545;
}

@ -0,0 +1,111 @@
/* JSDoc prettify.js theme */
/* plain text */
.pln {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* string content */
.str {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a keyword */
.kwd {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a comment */
.com {
font-weight: normal;
font-style: italic;
}
/* a type name */
.typ {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* a literal value */
.lit {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* punctuation */
.pun {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* lisp open bracket */
.opn {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* lisp close bracket */
.clo {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a markup tag name */
.tag {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a markup attribute name */
.atn {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a markup attribute value */
.atv {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a declaration */
.dec {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a variable name */
.var {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* a function name */
.fun {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0;
}

@ -0,0 +1,132 @@
/* Tomorrow Theme */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* Pretty printing styles. Used with prettify.js. */
/* SPAN elements with the classes below are added by prettyprint. */
/* plain text */
.pln {
color: #4d4d4c; }
@media screen {
/* string content */
.str {
color: #718c00; }
/* a keyword */
.kwd {
color: #8959a8; }
/* a comment */
.com {
color: #8e908c; }
/* a type name */
.typ {
color: #4271ae; }
/* a literal value */
.lit {
color: #f5871f; }
/* punctuation */
.pun {
color: #4d4d4c; }
/* lisp open bracket */
.opn {
color: #4d4d4c; }
/* lisp close bracket */
.clo {
color: #4d4d4c; }
/* a markup tag name */
.tag {
color: #c82829; }
/* a markup attribute name */
.atn {
color: #f5871f; }
/* a markup attribute value */
.atv {
color: #3e999f; }
/* a declaration */
.dec {
color: #f5871f; }
/* a variable name */
.var {
color: #c82829; }
/* a function name */
.fun {
color: #4271ae; } }
/* Use higher contrast and text-weight for printable form. */
@media print, projection {
.str {
color: #060; }
.kwd {
color: #006;
font-weight: bold; }
.com {
color: #600;
font-style: italic; }
.typ {
color: #404;
font-weight: bold; }
.lit {
color: #044; }
.pun, .opn, .clo {
color: #440; }
.tag {
color: #006;
font-weight: bold; }
.atn {
color: #404; }
.atv {
color: #060; } }
/* Style */
/*
pre.prettyprint {
background: white;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 12px;
line-height: 1.5;
border: 1px solid #ccc;
padding: 10px; }
*/
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0; }
/* IE indents via margin-left */
li.L0,
li.L1,
li.L2,
li.L3,
li.L4,
li.L5,
li.L6,
li.L7,
li.L8,
li.L9 {
/* */ }
/* Alternate shading for lines */
li.L1,
li.L3,
li.L5,
li.L7,
li.L9 {
/* */ }

@ -0,0 +1,9 @@
{
"name": "eecs448-project-1",
"version": "0.1.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"jsdoc": "^3.6.5"
}
}

@ -0,0 +1,156 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/parser@^7.9.4":
version "7.11.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
bluebird@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
catharsis@^0.8.11:
version "0.8.11"
resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.11.tgz#d0eb3d2b82b7da7a3ce2efb1a7b00becc6643468"
integrity sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==
dependencies:
lodash "^4.17.14"
entities@~2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
escape-string-regexp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
graceful-fs@^4.1.9:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
js2xmlparser@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.1.tgz#670ef71bc5661f089cc90481b99a05a1227ae3bd"
integrity sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==
dependencies:
xmlcreate "^2.0.3"
jsdoc@^3.6.5:
version "3.6.5"
resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.6.5.tgz#e004372ca6f2dcdf19b3d2ebcd7c725528485502"
integrity sha512-SbY+i9ONuxSK35cgVHaI8O9senTE4CDYAmGSDJ5l3+sfe62Ff4gy96osy6OW84t4K4A8iGnMrlRrsSItSNp3RQ==
dependencies:
"@babel/parser" "^7.9.4"
bluebird "^3.7.2"
catharsis "^0.8.11"
escape-string-regexp "^2.0.0"
js2xmlparser "^4.0.1"
klaw "^3.0.0"
markdown-it "^10.0.0"
markdown-it-anchor "^5.2.7"
marked "^0.8.2"
mkdirp "^1.0.4"
requizzle "^0.2.3"
strip-json-comments "^3.1.0"
taffydb "2.6.2"
underscore "~1.10.2"
klaw@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146"
integrity sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==
dependencies:
graceful-fs "^4.1.9"
linkify-it@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
dependencies:
uc.micro "^1.0.1"
lodash@^4.17.14:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
markdown-it-anchor@^5.2.7:
version "5.3.0"
resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz#d549acd64856a8ecd1bea58365ef385effbac744"
integrity sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==
markdown-it@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
dependencies:
argparse "^1.0.7"
entities "~2.0.0"
linkify-it "^2.0.0"
mdurl "^1.0.1"
uc.micro "^1.0.5"
marked@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355"
integrity sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
mkdirp@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
requizzle@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.3.tgz#4675c90aacafb2c036bd39ba2daa4a1cb777fded"
integrity sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==
dependencies:
lodash "^4.17.14"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
strip-json-comments@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
taffydb@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268"
integrity sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
underscore@~1.10.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf"
integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==
xmlcreate@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.3.tgz#df9ecd518fd3890ab3548e1b811d040614993497"
integrity sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==

@ -7,5 +7,4 @@ The files in this directory are external libraries used in this project.
- A kind-of crappy loader for defining Vue components using ES6 classes.
- Also used under the terms of the MIT license.
- https://code.garrettmills.dev/garrettmills/vues6
Sound effects obtained from https://www.zapsplat.com
- Sound effects obtained from https://www.zapsplat.com

@ -39,7 +39,12 @@ const template = `
</div>
</div>
`
export default class GameBoardComponent extends Component {
/**
* A component which represents a single, programmable game board.
* @extends Component
*/
class GameBoardComponent extends Component {
static get selector() { return 'app-game-board' }
static get template() { return template }
static get props() { return ['rows', 'is_placement_mode', 'ships_to_place', 'is_missile_mode'] }
@ -60,7 +65,7 @@ export default class GameBoardComponent extends Component {
/**
* Array of coordinates as [row_index, column_index] of cells which should
* show a ghost ship overlay.
* @type {[number, number][]}
* @type {number[]}
*/
ship_ghost_cells = []
@ -83,6 +88,10 @@ export default class GameBoardComponent extends Component {
*/
bound_fns = []
/**
* Called when the component is initialized.
* @return {Promise<void>}
*/
async vue_on_create() {
this.ready = true
@ -96,6 +105,10 @@ export default class GameBoardComponent extends Component {
window.addEventListener('keydown', keydown_fn)
}
/**
* Called when the component is destroyed.
* @return {Promise<void>}
*/
async vue_on_destroy() {
// Remove the event listeners for the shift key
const [keyup_fn, keydown_fn] = this.bound_fns
@ -103,6 +116,12 @@ export default class GameBoardComponent extends Component {
window.removeEventListener('keydown', keydown_fn)
}
/**
* Called when a user clicks a cell. If in placement mode, will attempt to place
* a ship. If in missile mode, will attempt to fire a missile.
* @param {number} row_i - the index of the row
* @param {number} cell_i - the index of the cell
*/
on_cell_click(row_i, cell_i) {
if ( this.is_placement_mode && this.ships_to_place[0] ) {
// We should try to place a ship here
@ -213,3 +232,5 @@ export default class GameBoardComponent extends Component {
}
}
}
export default GameBoardComponent

@ -14,7 +14,12 @@ const template = `
</div>
`
export default class GridCellComponent extends Component {
/**
* A component which represents a single, programmable grid cell.
* @extends Component
*/
class GridCellComponent extends Component {
static get selector() { return 'app-game-cell' }
static get template() { return template }
@ -29,15 +34,27 @@ export default class GridCellComponent extends Component {
/** Make the "GridCellState" enum available in the template. */
GridCellState = GridCellState
/**
* Fire a click event.
*/
on_click() {
this.$emit('click')
}
/**
* Fire a hover event.
* @param $event
*/
on_hover($event) {
this.$emit('hover', $event)
}
/**
* Fire a "hoverchange" event.
*/
on_mouse_leave() {
this.$emit('hoverchange')
}
}
export default GridCellComponent

@ -27,20 +27,58 @@ const template = `
</table>
</div>
`
export default class ScoreBoardComponent extends Component {
/**
* A component which represents the programmable scoreboard.
* @extends Component
*/
class ScoreBoardComponent extends Component {
static get selector() { return 'app-scoreboard' }
static get template() { return template }
static get props() { return [] }
/**
* The score of player one.
* @type {number}
*/
player_one_score = 0
/**
* The score of player two.
* @type {number}
*/
player_two_score = 0
/**
* The progress of player one, as a decimal.
* @type {number}
*/
player_one_progress = 0
/**
* The progress of player two, as a decimal.
* @type {number}
*/
player_two_progress = 0
/**
* The current player.
* @type {string|undefined}
*/
current_player = undefined
/**
* The winning player.
* @type {string|undefined}
*/
winning_player = undefined
Player = Player
/**
* Called when the component is initialized.
* @return {Promise<void>}
*/
async vue_on_create() {
game_service.on_state_change(() => {
this.update()
@ -49,6 +87,9 @@ export default class ScoreBoardComponent extends Component {
this.update()
}
/**
* Fetch new data from the game service.
*/
update() {
// here is where you should fetch the data from the game service and update variables on the class
this.player_one_score = game_service.get_player_score(Player.One)
@ -64,3 +105,5 @@ export default class ScoreBoardComponent extends Component {
}
}
}
export default ScoreBoardComponent

@ -80,7 +80,12 @@ const template = `
</div>
</div>
`
export default class TopLevelComponent extends Component {
/**
* Top-level component which manages the display of the entire game.
* @extends Component
*/
class TopLevelComponent extends Component {
static get selector() { return 'app-top-level' }
static get template() { return template }
static get props() { return [] }
@ -145,8 +150,11 @@ export default class TopLevelComponent extends Component {
*/
current_opponent_display = ''
/**
* Called when the component is initialized.
* @return {Promise<void>}
*/
async vue_on_create() {
console.log('game service', game_service)
this.current_state = game_service.get_game_state()
// Called every time the game state is updated
@ -221,3 +229,5 @@ export default class TopLevelComponent extends Component {
game_service.advance_game_state()
}
}
export default TopLevelComponent

@ -1,3 +1,5 @@
/** @module errors */
/**
* Placeholder class for an error that is thrown when a ship is placed
* in an invalid position.

@ -1,3 +1,5 @@
/** @module lang **/
import { GameState } from './util.js'
/**

@ -1,3 +1,5 @@
/** @module sounds */
import { appUrl } from './util.js'
/**
@ -42,6 +44,10 @@ class Sound {
}
}
/**
* Enum of the various sound effects available in the game.
* @type {object}
*/
const GameSounds = {
Victory: new Sound(appUrl('/lib/sounds/cartoon_success_fanfair.mp3')),
Fire: new Sound(appUrl('/lib/sounds/zapsplat_warfare_mortar_projectile_launch_002_25232.mp3')),
@ -49,6 +55,4 @@ const GameSounds = {
Miss: new Sound(appUrl('/lib/sounds/zapsplat_nature_water_pour_medium_amount_deep_sudden_fast_002_52765.mp3')),
}
console.log(GameSounds)
export { GameSounds }

@ -1,3 +1,5 @@
/** @module util */
/**
* Enum of all possible states of a grid cell.
* @type {object}

@ -22,7 +22,7 @@ export class GameStateService {
/**
* An array of all players. This is mostly for internal purposes.
* @private
* @type {(string)[]}
* @type {string[]}
*/
players = [Player.One, Player.Two]
@ -71,7 +71,7 @@ export class GameStateService {
/**
* Given the number of boats set by the player (n_boats), return an array
* of possible ShipTypes.
* @return {ShipType[]}
* @return {string[]}
*/
get_possible_boats(){
if (this.get_n_boats() === 1) {
@ -115,7 +115,7 @@ export class GameStateService {
/**
* If the current state is the PromptPlayerChange, then this is
* the state that we should move to next.
* @type {undefined|GameState}
* @type {undefined|string}
*/
post_player_change_state = undefined
@ -145,7 +145,7 @@ export class GameStateService {
/**
* Get the dimensions of the board as [rows, cols].
* @example const [n_rows, n_cols] = game_service.get_dimensions()
* @return {[number, number]}
* @return {number[]}
*/
get_dimensions() {
return [this.n_rows, this.n_cols]
@ -220,7 +220,7 @@ export class GameStateService {
*/
get_player_score(player) {
let score = 0
this.player_x_game_board[player].some(row => {
this.player_x_game_board[this.get_other_player(player)].some(row => {
row.some(cell => {
if ( cell.render === GridCellState.Damaged || cell.render === GridCellState.Sunk ) {
score += 1
@ -239,7 +239,7 @@ export class GameStateService {
*/
get_boat_count(player){
let boat_count = 0
this.player_x_game_board[player].some(row => {
this.player_x_game_board[this.get_other_player(player)].some(row => {
row.some(cell => {
if ( isShipCell(cell.render) ) {
boat_count += 1
@ -266,7 +266,7 @@ export class GameStateService {
/**
* Get the current game state.
* @return {GameState}
* @return {string}
*/
get_game_state() {
return clone(this.current_state)
@ -275,7 +275,6 @@ export class GameStateService {
/**
* responsible for advancing the game state
* will be consisting of
* @return
*/
advance_game_state() {
/** functions to be made that validate:
@ -383,7 +382,7 @@ export class GameStateService {
* If I want to fire a missile at row 5 column 7, then:
* game_service.attempt_missile_fire([5, 7])
*
* @param {[number, number]} coords
* @param {number[]} coords
* @return {boolean}
*/
attempt_missile_fire([target_row_i, target_col_i]) {
@ -419,7 +418,7 @@ export class GameStateService {
/**
* Checks the player's ships. If any are fully damaged, it flags that ship's cells
* as "sunk" rather than damaged.
* @param {Player} player
* @param {string} player
* @private
*/
_sink_damaged_ships(player) {
@ -448,9 +447,9 @@ export class GameStateService {
* to row 3 column 4, then I would call:
* game_service.place_ship(ShipType.x3, [3,2], [3,4])
*
* @param {ShipType} ship_type
* @param {[number, number]} coords_one
* @param {[number, number]} coords_two
* @param {string} ship_type
* @param {number[]} coords_one
* @param {number[]} coords_two
*/
place_ship(ship_type, coords_one, coords_two) {
// make sure the coordinates are valid for the given ship type
@ -490,9 +489,9 @@ export class GameStateService {
* game_service.get_covered_cells([1,1], [4,1])
* Which would return [[1,1], [2,1], [3,1], [4,1]].
*
* @param {[number, number]} coords_one
* @param {[number, number]} coords_two
* @return {[number, number][]}
* @param {number[]} coords_one
* @param {number[]} coords_two
* @return {number[][]}
*/
get_covered_cells(coords_one, coords_two) {
const [row_one, col_one] = coords_one
@ -522,9 +521,9 @@ export class GameStateService {
* to row 3 column 4, then I would call:
* game_service.validate_coordinates(ShipType.x3, [3,2], [3,4])
*
* @param {ShipType} ship_type
* @param {[number, number]} coords_one
* @param {[number, number]} coords_two
* @param {string} ship_type
* @param {number[]} coords_one
* @param {number[]} coords_two
*/
validate_coordinates(ship_type, coords_one, coords_two) {
if ( !isShipType(ship_type) ) throw new InvalidShipPlacementError('Invalid ship type: '+ship_type)
@ -570,7 +569,7 @@ export class GameStateService {
/**
* Get the number of cells the given ship type should occupy.
* @param {ShipType} ship_type
* @param {string} ship_type
* @return {number}
*/
get_ship_length(ship_type) {
@ -583,8 +582,8 @@ export class GameStateService {
/**
* Get the coordinates of all cells that have ships in them, for the given player.
* @param {Player} player
* @return {[number, number]}
* @param {string} player
* @return {number[]}
*/
get_ship_cells(player) {
const cells = []
@ -601,7 +600,7 @@ export class GameStateService {
/**
* If there is a winner, this will return the Player that won.
* If no winner has been decided yet, will return undefined.
* @return {Player|undefined}
* @return {string|undefined}
*/
get_winner() {
const [player_1, player_2] = this.players
@ -622,13 +621,13 @@ export class GameStateService {
(player_2_ship_cells.length > 0)
&& player_2_ship_cells.every(cell => this._get_cell_state(player_2, cell[0], cell[1]).render === GridCellState.Sunk)
)
if ( player_2_loses ) return player_2
if ( player_2_loses ) return player_1
}
/**
* Returns the other player.
* @param {Player} player
* @return {Player}
* @param {string} player
* @return {string}
*/
get_other_player(player) {
if ( player === Player.One ) return Player.Two
@ -637,7 +636,7 @@ export class GameStateService {
/**
* Given a Player type, return the display value of that player.
* @param {Player} player
* @param {string} player
* @return {string}
*/
get_player_display(player) {
@ -647,7 +646,7 @@ export class GameStateService {
/**
* Get an array of ship entities for the given player.
* @param {Player} player
* @param {string} player
* @return {object[]}
*/
get_ship_entities(player) {
@ -672,10 +671,10 @@ export class GameStateService {
/**
* Set the state of the cell at the given coordinates on the player's board
* to the specified state.
* @param {Player} player
* @param {string} player
* @param {number} row_i
* @param {number} col_i
* @param {GridCellState} state
* @param {string} state
* @private
*/
_set_cell_state(player, row_i, col_i, state) {
@ -684,7 +683,7 @@ export class GameStateService {
/**
* Get the state of the cell at the given coordinates on the player's board.
* @param {Player} player
* @param {string} player
* @param {number} row_i
* @param {number} col_i
* @return {object}

Loading…
Cancel
Save