Battleships is a classic MB board game.

This project came about at the pub, and was set a challenge to develop the game from scratch
I instantly saw how I could develop this and accepted the challenge. Part of the challenge is to include a "Cheating AI" in which the CPU will relocate the position of the ships
in the future (this is not part of version 1)

Total development time: 8 hours
1 hr - Design
1 hr - CPU board and Controls replicated
2 hrs - Player Controls
2 hrs - AI
2 hrs - Debugging and testing

To do list:

* Cheating AI
* Improve the AI to attack the other side of the ship once a miss has been detected
* Bug fix on vertical ships spanning 2 columns
* Add Co-ordinates around the board (A-J) and (0-9)
* Add functional controls (Start game, Position player ships)
* Working board
* Sound effects

* Online game play (New script for PHP, MySQL, and AJAX)

Feel free to use the script, a complimentary commentary would be appreciated
Credit: Adam Mackay (Developer)

Please post any comments on your thoughts of the code, positive and constructive criticism welcome as it only goes to assist the development of the code.


HTML index.html
HTML Code:
<HTML>
	<Head>
		<Title>Battleships</title>
		<link rel="stylesheet" href="style.css">
		<script src="script.js"></script>
	</Head>
	<body>
		<div id="game">
			<div id="openent">
				<div id="topgrid">
					<div id="Openent1" class="gridRow"></div>
					<div id="Openent2" class="gridRow"></div>
					<div id="Openent3" class="gridRow"></div>
					<div id="Openent4" class="gridRow"></div>
					<div id="Openent5" class="gridRow"></div>			
					<div id="Openent6" class="gridRow"></div>	
					<div id="Openent7" class="gridRow"></div>	
					<div id="Openent8" class="gridRow"></div>	
					<div id="Openent9" class="gridRow"></div>	
					<div id="Openent10" class="gridRow"></div>				
				</div>
				<div id="PlayerPercentage" class="percentageBar">
					<div id="PlayerCompleted" class="progressBar"></div>
				</div>
			</div>
			<div id="scoreboard">
				<div id="playerBoard"></div>
				<div id="CPUBoard"></div>
			</div>
			<div id="player">
				<div id="bottomgrid">
					<div id="Player1" class="gridRow"></div>
					<div id="Player2" class="gridRow"></div>
					<div id="Player3" class="gridRow"></div>
					<div id="Player4" class="gridRow"></div>
					<div id="Player5" class="gridRow"></div>			
					<div id="Player6" class="gridRow"></div>	
					<div id="Player7" class="gridRow"></div>	
					<div id="Player8" class="gridRow"></div>	
					<div id="Player9" class="gridRow"></div>	
					<div id="Player10" class="gridRow"></div>				
				</div>
			</div>
		</div>
	</body>
</html>
CSS: style.css
Code:
body{
	background: #C0C0C0;
	background-image: url('images/background.jpg');
	background-size: cover;
}
#topgrid, #bottomgrid{
	height: 620px;
	width: 620px;
	background: navy;
}
#player{
	position: relative;
	float: right;
}
#game{
	background: rgba(255,255,255,0.5);
}
#openent{
	position: relative;
	float: left;
}
.percentageBar{
	width: 620px;
	height: 40px;
	background: grey;
}
.progressBar{
	background: red;
	height: 100%;
	width: 0%;
}
.grid{
	position: relative;
	height: 60px;
	width: 60px;
	border: solid 1px white;
//	background-image: url('images/water.jpg');
	background-image: url('images/sea.jpg');

}
.gridRow{
	position: relative;
	float: left;
}
.miss{
	background: cyan;
//	background-image: url('images/miss.jpg');
//	background-size: cover;

}
.hit{
	background: orange;
	background-image: url('images/fire.jpg');
	background-size: cover;

}
.cubeno{
	display: none;
	height: 100%;
	width: 100%;
	font-size: 18pt;
	color: white;
	font-weight: bold;
	font-family: arial;
	padding: 15px;
}
Javascript Script.js
Code:
			arrOpenentGrid = [];
			arrPlayerGrid = [];	
			arrShipLengths = [2,3,3,4,5];
			arrAiShipNames = [
				{Piece: "Patrol Boat", 		CoOrdinates: [], Destroyed: 0, Total: 0},
				{Piece: "Destroyer", 		CoOrdinates: [], Destroyed: 0},
				{Piece: "Submarine", 		CoOrdinates: [], Destroyed: 0},
				{Piece: "Battleship", 		CoOrdinates: [], Destroyed: 0},
				{Piece: "Aircraft Carrier", CoOrdinates: [], Destroyed: 0}
			];			
			arrPlayerShipNames = [
				{Piece: "Patrol Boat", 		CoOrdinates: [], Destroyed: 0, Total: 0},
				{Piece: "Destroyer", 		CoOrdinates: [], Destroyed: 0},
				{Piece: "Submarine", 		CoOrdinates: [], Destroyed: 0},
				{Piece: "Battleship", 		CoOrdinates: [], Destroyed: 0},
				{Piece: "Aircraft Carrier", CoOrdinates: [], Destroyed: 0}
			];
				
			arrAI_LastHit_Neighbours = [];
			blOnline = false;
			blGameActive = true; 
			blAiPlaying = true;		
			intAI_LastHit = -1;	
			intAI_Tries = 0;	

			for (i = 0; i<=99; i++){
				arrOpenentGrid.push (i);
				arrPlayerGrid.push(i);
			}

			for (i = 0; i<=arrShipLengths.length-1; i++){
				funAIShips();
				funPlayerShips();
			}
			console.log ("Game: AI co-ordinates are set");
			console.log ("Game: Player co-ordinates are set");

			function funGridMaker(){
				for (p = 1; p <= 10; p++){
					gridOpenent = document.getElementById("Openent" + p);
					gridPlayer = document.getElementById("Player" + p);
					for (i = 0; i <10; i++){
						gridOpenent.innerHTML += "<div id='openentTile" + (p-1) + "" + i + "' class='grid' onclick='funPlayersTurn("+ (p-1) + "" + i + ", this)'><div class='cubeno'>" + (p-1) + "" + i +"</div></div>";
						gridPlayer.innerHTML += "<div id='playerTile" + (p-1) + "" + i + "' class='grid'><div class='cubeno'>" + (p-1) + "" + i +"</div></div>";
					}
				}
			}
			function funPlayerShips(blHuman = false){
				arrShipCoOrdinates = funCreateShipCoOrdinates(i);
				funValidateShip(arrShipCoOrdinates, blHuman);
			}
			function funAIShips(blHuman = true){
				arrShipCoOrdinates = funCreateShipCoOrdinates(i);
				funValidateShip(arrShipCoOrdinates, blHuman);
			}			

			function funCreateShipCoOrdinates(intShipNo){
				blOrientation = parseInt(Math.random()* 100) % 2;				
				intBoardPossition = parseInt(Math.random() * 50);
				intShipSize = arrShipLengths[intShipNo];				
				arrShip =[];
				if (blOrientation == 1){
					intIncrement = 10;
				}else{
					intIncrement = 1;
				}

				for (Q = 1; Q <= intShipSize; Q++){							
					if(intIncrement ==1){
						if (10 - (intBoardPossition % 10) < intShipSize){
							arrShip = funCreateShipCoOrdinates();
							return;
						}
					}
					arrShip.push (intBoardPossition + (Q * intIncrement));
				}
				return arrShip;
			}
			function funValidateShip(arrShipCoOrdinates, blHuman = true){
				blOKToPlot = true;
				console.log ("Validating ship co-ordinates: " + arrShipCoOrdinates);
				if (Math.max.apply(null, arrShipCoOrdinates) > 100){ 
					if (blHuman){
						funPlayerShips();
					}else{
						funAIShips();
					}
					console.log ("Game: Ship co-ordinates are out of range");
					return;
				}
				if (Math.min.apply(null, arrShipCoOrdinates) < 0){ 
					if (blHuman){
						funPlayerShips();
					}else{
						funAIShips();
					}
					console.log ("Game: Ship co-ordinates are out of range");
					return;
				}
				if (typeof(arrShipCoOrdinates) == "undefined"){
					arrShipCoOrdinates = funCreateShipCoOrdinates(i);
				}
				
				for (h = 0; h <= arrShipCoOrdinates.length-1; h++){
					if (blHuman){
						for (z = 0; z < 5; z++){
							if (arrAiShipNames[z].CoOrdinates.indexOf(arrShipCoOrdinates[h]) > -1){
								console.log ("Game: Ship generated overlaps a (" + arrAiShipNames[z].Piece + ") for the AI, redrawing new co-ordinates");
								funAIShips();
								return;
							}
						}
					}else{
						for (z = 0; z < 5; z++){
							if (arrPlayerShipNames[z].CoOrdinates.indexOf(arrShipCoOrdinates[h]) > -1){
								console.log ("Game: Ship generated overlaps a (" + arrPlayerShipNames[z].Piece + ") for the player, redrawing new co-ordinates");
								funPlayerShips();
								return;
							}
						}
					}
				}
				console.log ("Game: Assigning Ship ("+arrAiShipNames[i].Piece + ") co-ordinates");
				for (Q = 0; Q <= arrShipCoOrdinates.length-1; Q++){
					if (blHuman){
						arrAiShipNames[i].CoOrdinates = arrShipCoOrdinates;
					}else{
						arrPlayerShipNames[i].CoOrdinates = arrShipCoOrdinates;
					}
				}
				if (blHuman){
					arrAiShipNames[0].Total += arrShipCoOrdinates.length;
				}else{
					arrPlayerShipNames[0].Total += arrShipCoOrdinates.length;
				}				
			}
			function funAttemptAIMove(){
				if (intAI_LastHit > -1){
					console.log ("AI: Deciding which neighbouring tile to attack");
					intTileGuess = parseInt(Math.random() * arrAI_LastHit_Neighbours.length);
					intTileGuess = arrAI_LastHit_Neighbours[intTileGuess];
					intBoardPossition = intTileGuess;
					intPlayerGridArrayIndex = arrPlayerGrid.indexOf(intTileGuess);
					if (arrAI_LastHit_Neighbours.length == 0){
						intAI_LastHit = -1;
						intAI_Tries = 0;
					}
					intAI_Tries++;
					if (intAI_Tries > 200){
						intAI_Tries = 0;
						intAI_LastHit =-1;
						console.log ("AI: moved to a calculated move to random attack");
					}
				}else{
					intRandomBox = parseInt(Math.random() * (arrPlayerGrid.length -1));				
					intBoardPossition = arrPlayerGrid[intRandomBox];					
					intPlayerGridArrayIndex = arrPlayerGrid.indexOf(intBoardPossition);
					console.log ("AI: Attacking tile " + intBoardPossition );
				}
				intAIshipsIndex = -1;
				for (z = 0; z < 5; z++){
					if (arrPlayerShipNames[z].CoOrdinates.indexOf(intBoardPossition) > -1){
						intAIshipsIndex = arrPlayerShipNames[z].CoOrdinates.indexOf(intBoardPossition);
						intPlayerIndex = z;
					}
				}
				if (intPlayerGridArrayIndex > -1){
					stBoardTile = "0" + intBoardPossition;
					if (stBoardTile.length == 3){
						stBoardTile = stBoardTile.substring(3,1);
					}else{
						stBoardTile = stBoardTile.substring(0,2);
					}
					elPlayerTile = document.getElementById("playerTile" + stBoardTile);
					if (intAIshipsIndex > -1){
						elPlayerTile.className = "grid hit";
						console.log ("AI: Openent ship hit");
						if (intAI_LastHit > -1){
							z = intAI_LastHit - intBoardPossition;
							arrAI_LastHit_Neighbours = [];
							if (z == -1 || z==1){
								arrAI_LastHit_Neighbours.push(intBoardPossition - 1);
								arrAI_LastHit_Neighbours.push(intBoardPossition + 1);
								console.log ("AI: Orientation determined as vertical");
							}else{
								arrAI_LastHit_Neighbours.push(intBoardPossition - 10);
								arrAI_LastHit_Neighbours.push(intBoardPossition + 10);
								console.log ("AI: Orientation determined as horizontal");
							}
						for (z = 0; z<5; z++){
							if (arrPlayerShipNames[z].CoOrdinates.length == 0){
								arrPlayerShipNames[z].Destroyed = 1;
								console.log ("AI: has destroyed the Players's " + arrPlayerShipNames[z].Piece);
							}
						}
						}else{ 
							intAI_LastHit = intBoardPossition;
							intAI_InitialHit = intBoardPossition;
							arrAI_LastHit_Neighbours = [];
							arrAI_LastHit_Neighbours.push(intBoardPossition - 1);
							arrAI_LastHit_Neighbours.push(intBoardPossition + 1);
							arrAI_LastHit_Neighbours.push(intBoardPossition - 10);
							arrAI_LastHit_Neighbours.push(intBoardPossition + 10);
						}
						console.log ("AI: Next tiles set");
						arrPlayerShipNames[intPlayerIndex].CoOrdinates.splice(intAIshipsIndex, 1)
						arrPlayerShipNames[0].Total -= 1;
						setTimeout(funAIsTurn, 1000);
					}else{
						elPlayerTile.className = "grid miss";
						console.log ("AI: Missed");
						console.log ("Player: Waiting for player to select a tile");
						blGameActive = true;
					}
					arrPlayerGrid.splice(intPlayerGridArrayIndex,1);
					if (arrPlayerShipNames[0].Total == 0){
						funGameOver("AI");
					}
					return intBoardPossition;
				}else{
					funAttemptAIMove();
					return;
				}
			}

			function funAIsTurn(){
				coords = funAttemptAIMove()
			}
			
			function funPlayersTurn(coords, el){
				if (blGameActive == false){
					console.log ("Player: Move rejected, reason: Not players turn or game has ended");
					return;															
				}
				missedCheck = arrOpenentGrid.indexOf(coords); 
				if (missedCheck == -1){
					console.log ("Player: Selected a played tile, waiting for player to choose another tile");
					return;
				}
				blHit = false;
				for (x=0; x<5; x++){
					ShipsCheck = arrAiShipNames[x].CoOrdinates.indexOf(coords); 
					if ( ShipsCheck > -1){
						el.className = "grid hit";
						console.log ("Player: openent ship hit");
						arrAiShipNames[x].CoOrdinates.splice(ShipsCheck, 1)
						arrAiShipNames[0].Total -= 1;
						if (arrAiShipNames[x].CoOrdinates.length == 0){
							arrAiShipNames[x].Destroyed = 1;
							console.log ("Player: has destroyed the AI's " + arrAiShipNames[x].Piece);
						}
						blHit = true;
					}
				}
				if (missedCheck > -1 && blHit == false){
					el.className = "grid miss";
					console.log ("Player: Missed");
					if (blAiPlaying) {
						blGameActive = false;
						console.log ("Game: AIs Turn");
						setTimeout(funAIsTurn, 1000);
					}													
				}
				arrOpenentGrid.splice(missedCheck,1);
				if (arrAiShipNames[0].Total ==0){	 
					funGameOver("Player");
				}
			}
			function funGameOver(stPlayer){
				alert ("Game Over: " + stPlayer + " Won!");
				blGameActive = false;
				console.log (stPlayer + ": has won the game");
				return;
			}
			document.addEventListener("DOMContentLoaded", funGridMaker);
Images: (Please forgive the flag on the ship I have not had time to photoshop it out as it was randomly downloaded off Goggle for the background of the game)
Click image for larger version. 

Name:	background.jpg 
Views:	23 
Size:	94.3 KB 
ID:	6229

Sea
Name:  sea.jpg
Views: 14
Size:  3.2 KB

Fire
Name:  fire.jpg
Views: 10
Size:  1.7 KB