<h1 class="title"><span class="blue">&lt;</span>Battleship<span class="blue">&gt;</span><span class="yellow">JS</span></h1>

  <h2>Created with &#10084; by <a href="http://rafaelmedina.me" target="_blank">Rafael Medina</a></h2>

  <div class="play_container">
    <div class="demo-flex-spacer"></div>


        <form class="webflow-style-input">
          <input type="text" placeholder="A0" id="guessInput" style='text-transform:uppercase'/>
          <button type="button" id="fireButton">
            <i class="icon ion-android-arrow-forward"></i>
          </button>
        </form>

    


  </div>
  <div id="messageArea" class="message"></div>
  <div id="board">

    <table class="container">
      <thead>
        <tr>
          <th>
          </th>
          <th>
            <h1>0</h1></th>
            <th>
              <h1>1</h1></th>
              <th>
                <h1>2</h1></th>
                <th>
                  <h1>3</h1></th>
                  <th>
                    <h1>4</h1></th>
                    <th>
                      <h1>5</h1></th>
                      <th>
                        <h1>6</h1></th>
                      </tr>
                    </thead>
                    <tbody>
                      <!-- battleship row 0 -->
                      <tr>
                        <td>A</td>
                        <td id="00">A0</td>
                        <td id="01">A1</td>
                        <td id="02">A2</td>
                        <td id="03">A3</td>
                        <td id="04">A4</td>
                        <td id="05">A5</td>
                        <td id="06">A6</td>
                      </tr>
                      <!-- battleship row 1 -->
                      <tr>
                        <td>B</td>
                        <td id="10">B0</td>
                        <td id="11">B1</td>
                        <td id="12">B2</td>
                        <td id="13">B3</td>
                        <td id="14">B4</td>
                        <td id="15">B5</td>
                        <td id="16">B6</td>
                      </tr>
                      <!-- battleship row 2 -->
                      <tr>
                        <td>C</td>
                        <td id="20">C0</td>
                        <td id="21">C1</td>
                        <td id="22">C2</td>
                        <td id="23">C3</td>
                        <td id="24">C4</td>
                        <td id="25">C5</td>
                        <td id="26">C6</td>
                      </tr>
                      <!-- battleship row 3 -->
                      <tr>
                        <td>D</td>
                        <td id="30">D0</td>
                        <td id="31">D1</td>
                        <td id="32">D2</td>
                        <td id="33">D3</td>
                        <td id="34">D4</td>
                        <td id="35">D5</td>
                        <td id="36">D6</td>
                      </tr>
                      <!-- battleship row 4 -->
                      <tr>
                        <td>E</td>
                        <td id="40">E0</td>
                        <td id="41">E1</td>
                        <td id="42">E2</td>
                        <td id="43">E3</td>
                        <td id="44">E4</td>
                        <td id="45">E5</td>
                        <td id="46">E6</td>
                      </tr>
                      <!-- battleship row 5 -->
                      <tr>
                        <td>F</td>
                        <td id="50">F0</td>
                        <td id="51">F1</td>
                        <td id="52">F2</td>
                        <td id="53">F3</td>
                        <td id="54">F4</td>
                        <td id="55">F5</td>
                        <td id="56">F6</td>
                      </tr>
                      <!-- battleship row 6 -->
                      <tr>
                        <td>G</td>
                        <td id="60">G0</td>
                        <td id="61">G1</td>
                        <td id="62">G2</td>
                        <td id="63">G3</td>
                        <td id="64">G4</td>
                        <td id="65">G5</td>
                        <td id="66">G6</td>
                      </tr>
                    </tbody>
                  </table>
                </div>
// reset
html { box-sizing: border-box; font-size: 10px; }
*, *:before, *:after { box-sizing: inherit; }
body, ul, li  { margin: 0; padding: 0; }
li { list-style: none; }
p, h1, h2, h3, h4, h5, h6 { margin-top: 0; }
a { text-decoration: none; }
input { border-style: none; background: transparent; outline: none; }
button { padding: 0; background: none; border: none; outline: none; }
form {margin: 0;padding: 0;border: 0;font-size: 100%; font: inherit; vertical-align: baseline;}

body {
  font-family: 'Open Sans', sans-serif;
  font-weight: 300;
  line-height: 1.42em;
  color: #A7A1AE;
  background-color: #1F2739;
}

h1 {
  font-size: 4em;
  font-weight: 300;
  line-height: 1em;
  text-align: center;
  color: #4DC3FA;
}

h2 {
  display: block;
  font-size: 1.1em;
  font-weight: 300;
  text-align: center;
  line-height: 1.1em;
  padding-bottom: 2.2em;
  color: #FEFEFE;
}

h2 a {
  font-weight: 700;
  text-transform: uppercase;
  color: #FFFFFF;
  text-decoration: none;
}

.blue { color: #185875;}
.yellow { color: #FFF842;}

.title {
  margin: 1em 1em;
}

.message {
  text-align: center;
}

.container th h1 {
  font-weight: bold;
  font-size: 1.2em;
  text-align: left;
  color: #185875;
}

.container td {
  font-weight: normal;
  font-size: 1.2em;
  -webkit-box-shadow: 0 2px 2px -2px #0E1119;
	   -moz-box-shadow: 0 2px 2px -2px #0E1119;
	        box-shadow: 0 2px 2px -2px #0E1119;
}

.container {
  display: table;
  text-align: left;
  overflow: hidden;
  width: 90%;
  margin: 0 auto;
  padding: 2em 0 8em 0;
}

.container td, .container th {
  padding-bottom: 2%;
  padding-top: 2%;
  padding-left: 2%;
  cursor: crosshair;
}

/* Background-color of the odd rows */
.container tr:nth-child(odd) {
  background-color: #323C50;
}

/* Background-color of the even rows */
.container tr:nth-child(even) {
	  background-color: #2C3446;
}

/* Avoid selection the first one*/
.container td:first-child {
  background-color: #1F2739;
  color: #FB667A;
}

.container th {
    text-align: center;
	  background-color: #1F2739;
}

/*** marking hovering horizontal*/
.container tr:hover:nth-child(n+1) {
  background-color: #464A52;
  -webkit-box-shadow: 0 6px 6px -6px #0E1119;
	   -moz-box-shadow: 0 6px 6px -6px #0E1119;
	        box-shadow: 0 6px 6px -6px #0E1119;
}

/*** marking vertical hovering
td:hover::after,
thead th:not(:empty):hover::after,
td:focus::after,
thead th:not(:empty):focus::after {
  content: "";
  position: absolute;
  background-color: #464A52;
  -webkit-box-shadow: 0 6px 6px -6px #0E1119;
	   -moz-box-shadow: 0 6px 6px -6px #0E1119;
	        box-shadow: 0 6px 6px -6px #0E1119;
  height: 10000px;
  width: 100%;
  z-index: -1;
}
**/

.container th:hover {
  background-color: #464A52;
  -webkit-box-shadow: 0 6px 6px -6px #0E1119;
	   -moz-box-shadow: 0 6px 6px -6px #0E1119;
	        box-shadow: 0 6px 6px -6px #0E1119;
}

.container td:hover:first-child {
  color: #FB667A;
  background-color: #464A52;
}
/***********High *********/
.container td:hover:nth-child(n+2) {
  background-color: #FFF842;
  color: #403E10;
  font-weight: bold;

}

/*** PLAY *****/

//colors
$input-background: rgba(57,63,84,0.8);
$input-text-inactive: #7881A1;
$input-text-active: #BFD2FF;

.demo-flex-spacer {
  flex-grow: 1;
}

.play_container {
  display: flex;
  flex-direction: column;
  //justify-content: center;
  height: 10vh;
  max-width: 400px;
  padding: 1em 1em;
  margin: 0 auto;
}

.webflow-style-input {
  position: relative;
  display: flex;
  flex-direction: row;
  width: 65%;
  max-width: 290px;
  margin: 0 auto;
  border-radius: 2px;
  padding: 0.4rem 1rem 0.6rem;
  background: $input-background;
  &:after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 99;
    height: 2px;
    border-bottom-left-radius: 2px;
    border-bottom-right-radius: 2px;
    background-position: 0% 0%;
    background: linear-gradient(to right, #B294FF, #57E6E6, #FEFFB8, #57E6E6, #B294FF, #57E6E6);
    background-size: 500% auto;
    animation: gradient 3s linear infinite;
  }
}

.webflow-style-input input {
  flex-grow: 1;
  color: $input-text-active;
  font-size: 1.8rem;
  line-height: 2.4rem;
  vertical-align: middle;
  &::-webkiyt-input-placeholder {
    color: $input-text-inactive;
  }
}

.webflow-style-input button {
  color: $input-text-inactive;
  font-size: 2.4rem;
  line-height: middle;
  transition: color .25s;
  &:hover {
    color: $input-text-active;
  }
}

.hit {
  content: "hit";
  font-size: 2em;
  background-color: green;
}
.miss {
  content: "miss";
  font-size: 2em;
  background-color: red;
}

@media (min-width: 700px) {
  .container {
    width: 70%;
  }
}

@media (min-width: 800px) {
  .container {
    width: 65%;
  }
}

@media (min-width: 900px) {
  .container {
    width: 55%;
  }
}

@media (min-width: 1024px) {
  .container {
    width: 50%;
  }
}
View Compiled
var view = {
		displayMessage: function(msg) {
				var messageArea = document.getElementById("messageArea");
				messageArea.innerHTML = msg;
		},
		displayHit: function(location) {
				//location is created from the row and column 
				//and matches an id of an id of a <td> element
				var cell = document.getElementById(location);
				cell.setAttribute("class", "hit");
		},

		displayMiss: function(location) {
				var cell = document.getElementById(location);
				cell.setAttribute("class", "miss");
		}
};

var model = {
		boardSize: 7,
		numShips: 3,
		shipLength: 3,
		shipsSunk: 0,
	/* Hardcoded values
		ships: [{
				locations: ["06", "16", "26"],
				hits: ["", "", ""]
		}, {
				locations: ["24", "34", "44"],
				hits: ["", "", ""]
		}, {
				locations: ["10", "11", "12"],
				hits: ["", "", ""]
		}],*/
		ships: [{ locations: [0, 0, 0], hits: ["", "", ""] },
					  { locations: [0, 0, 0], hits: ["", "", ""] },
					  { locations: [0, 0, 0], hits: ["", "", ""] } ],

		fire: function(guess) {
				for (var i = 0; i < this.numShips; i++) {
						var ship = this.ships[i];
						var index = ship.locations.indexOf(guess);
						// Check to see if the ship has already been hit,
						//message the user, and return true.
						if (ship.hits[index] === "hit") {
								view.displayMessage("Oops, you already hit that location!");
								return true;
						} else if (index >= 0) {
								ship.hits[index] = "hit";
								view.displayHit(guess);
								view.displayMessage("HIT!");

								if (this.isSunk(ship)) {
										view.displayMessage("You sank my battleship!");
										this.shipsSunk++;
								}
								return true;
						}
				}
				view.displayMiss(guess);
				view.displayMessage("You missed.");
				return false;
		},

		isSunk: function(ship) {
				for (var i = 0; i < this.shipLength; i++) {
						if (ship.hits[i] !== "hit") {
								return false;
						}
				}
				return true;
		},

		generateShipLocation: function() {
				var locations;
				for (var i = 0; i < this.numShips; i++) {
						do {
								locations = this.generateShip();
						} while (this.collision(locations));
						this.ships[i].locations = locations;
				}
		},

		generateShip: function() {
				var direction = Math.floor(Math.random() * 2);
				var row, col;
				if (direction === 1) {
						// Generate ship's location horizontally
						row = Math.floor(Math.random() * this.boardSize);
						col = Math.floor(Math.random() * (this.boardSize - this.shipLenght));
				} else {
						//Generate a starting location vertically
						row = Math.floor(Math.random() * (this.boardSize - this.shipLenght));
						col = Math.floor(Math.random() * this.boardSize);
				}

				var newShipLocations = [];
				for (var i = 0; i < this.shipLength; i++) {
						if (direction === 1) {
								// Generate ship's location horizontally
								newShipLocations.push(row + "" + (col + i));
						} else {
								//Generate a starting location vertically
								newShipLocations.push((row + i) + "" + col);
						}
				}
				return newShipLocations;
		},
	
		collision: function(locations) {
			for (var i = 0; i < this.numShips; i++) {
				var ship = model.ships[i];
				for (var j = 0; j < locations.length; j++) {
					if (ship.locations.indexOf(locations[j]) >= 0) {
						return true;
					}
				}
			}
		}
}

var controller = {
		guesses: 0,
		processGuess: function(guess) {
				var location = parseGuess(guess);
				if (location) {
						this.guesses++;
						var hit = model.fire(location);
						if (hit && model.shipsSunk === model.numShips) {
								view.displayMessage("You sank all my battleships, in " + this.guesses + " guesses");
						}
				}
		}
};
// helper function to parse a guess from the user

function parseGuess(guess) {
		var alphabet = ["A", "B", "C", "D", "E", "F", "G"];
		if (guess === null || guess.length !== 2) {
				alert("Oops, please enter a letter and a number on the board.");
		} else {
				var firstChar = guess.charAt(0);
				var row = alphabet.indexOf(firstChar);
				var column = guess.charAt(1);

				if (isNaN(row) || isNaN(column)) {
						alert("Oops, that isn't on the board.");
				} else if (row < 0 || row >= model.boardSize ||
						column < 0 || column >= model.boardSize) {
						alert("Oops, that's off the board!");
				} else {
						return row + column;
				}
		}
		return null;
}

function handleKeyPress(e) {
		var fireButton = document.getElementById("fireButton");
		if (e.keyCode === 13) {
				fireButton.click();
				return false;
		}
}

function handleFireButton() {
		var guessInput = document.getElementById("guessInput");
		var guess = guessInput.value;
		controller.processGuess(guess);
}

window.onload = init;

function init() {
		var fireButton = document.getElementById("fireButton");
		fireButton.onclick = handleFireButton;
		var guessInput = document.getElementById("guessInput");
		guessInput.onkeypress = handleKeyPress;
}

Run Pen

External CSS

  1. https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css

External JavaScript

This Pen doesn't use any external JavaScript resources.