<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Kevin's Battleship</title> <style>
#battleship{ position:relative; background-color:lightgrey; font-size:1em; width:650px; height:420px; border:1px solid black; text-align:left; user-select:none; font-family:sans-serif; overflow:hidden; } #bs_header{ position:absolute; width:100%; height:30px; left:0; top:0; overflow:hidden; border:1px solid black; background-color: grey; } #bs_title{ position:absolute; left:0; top:0; width:250px; height:100%; font-weight:bold; font-style:italic; font-size:1.5em; padding-left:5px; color:darkgrey; text-shadow:0 0 2px black; } #bs_newGameButton{ position:absolute; right:40px; top:5%; width:100px; height:70%; font-size:1em; text-align:center; background-color:lightgrey; border:2px outset grey; border-radius:8px; cursor:pointer; } #bs_help{ position:absolute; right:0; top:0; width:30px; height:100%; font-weight:bold; font-size:1.5em; text-align:center; background-color:dodgerblue; color:white; border-radius:999px; cursor:pointer; } #bs_message{ position:absolute; left:150px; top:40px; width:calc(100% - 300px - 10px - 20px); height:40px; padding:0 10px; font-size:1em; font-weight:bold; text-align:center; background-color:rgb(22, 48, 22); color:rgb(48, 202, 48); border:5px inset grey; border-radius:999px; animation: bs_messageFlash 0.5s linear 0s 1 normal; }@keyframes bs_messageFlash{ from{background-color:rgb(123, 255, 123);} to{background-color:rgb(22, 48, 22);} } #bs_fastDiv{ position:absolute; right:10px; top:50px; } #bs_leftBoard,#bs_rightBoard{ position:absolute; width:calc(30px * 10); height:calc(30px * 10); top:100px; background-color: midnightblue; border:5px inset grey; cursor:pointer; }#bs_leftBoard{ left:10px; }#bs_rightBoard{ right:10px; } .bs_board_block{ cursor:not-allowed !important; }.bs_board_border{ animation: bs_board_border 0.5s ease-out 0s infinite alternate; background-color: rgb(36, 36, 146) !important; }@keyframes bs_board_border{ from{outline:0.1px solid black;} to{outline:8px solid black;} } .bs_bgCell{ position:absolute; width:28px; height:28px; border:1px solid black; /*animation: bs_waves 3s ease-in 0s infinite alternate;*/ }@keyframes bs_waves{ from{background-color:rgba(255,255,255,0);} to{background-color:rgba(255,255,255,0.1);} } .bs_leftCell,.bs_rightCell{ position:absolute; width:30px; height:30px; } .bs_has_white{ background-image:radial-gradient(white 30%,black 35%,rgba(0,0,0,0) 36%); }.bs_has_red{ background-image:radial-gradient(red 30%,black 35%,rgba(0,0,0,0) 36%); }.bs_has_land{ background-image:radial-gradient(forestgreen 30%,darkgreen 79%,rgba(0,0,0,0) 80%); } #bs_leftExplosionHolder,#bs_rightExplosionHolder{ position:absolute; transform:scale(60%,60%); pointer-events:none; } .bs_explosion{ position:absolute; width:10px; height:10px; border-radius:999px; animation: bs_explosion 0.5s ease-out 0s infinite alternate; border:2px solid red; border-width:0 0 2px 0; }@keyframes bs_explosion{ 0%{background-color:yellow;} 100%{background-color:orangered;} } .bs_waterExplosion{ position:absolute; width:10px; height:10px; border-radius:999px; animation: bs_waterExplosion 0.5s ease-out 0s infinite alternate; border:2px solid blue; border-width:0 0 2px 0; }@keyframes bs_waterExplosion{ 0%{background-color:white;} 100%{background-color:dodgerblue;} } #bs_placingMenu{ position:absolute; background-color:darkslategrey; left:30px; top:120px; width:270px; height:270px; border-radius:20px; outline:5px solid dimgrey; box-shadow: 0px 0px 10px 0px black, 0px 0px 10px 0px black;/* X, Y, outer extent, inner extent, color */ } .bs_placingMenu_ship{ position:absolute; left:60px; cursor:pointer; }.bs_placingMenu_ship:hover{ outline:5px double dodgerblue; }.bs_placingMenu_placedShip{ opacity:20%; } #bs_placingMenu_ship1{top:calc(55px + 0px + 0px);} #bs_placingMenu_ship2{top:calc(55px + 30px + 5px);} #bs_placingMenu_ship3{top:calc(55px + 60px + 10px);} #bs_placingMenu_ship4{top:calc(55px + 90px + 15px);} #bs_placingMenu_ship5{top:calc(55px + 120px + 20px);} #bs_placingMenu_rotate{ position:absolute; bottom:10px; right:10px; overflow:hidden; text-align:center; cursor:pointer; }#bs_placingMenu_rotateSymbol{ font-size:xx-large; } .bs_you_ship,.bs_cpu_ship{ pointer-events:none; } .bs_placedShip{} .bs_unplacedShip{ opacity:0%; }.bs_heldShip{ animation: bs_blinking 0.4s ease-out 0s infinite alternate; }@keyframes bs_blinking{ 0%{opacity:20%;} 100%{opacity:100%;} }.bs_shipRotated{ transform-origin:15px 15px; transform:rotate(-90deg); } .bs_ship1,.bs_ship2,.bs_ship3,.bs_ship4,.bs_ship5{ position:absolute; width:150px; height:30px; overflow:hidden; background-size:cover; background-repeat:no-repeat; }.bs_ship1{background-image:url("battleships/ship_carrier.png");} .bs_ship2{background-image:url("battleships/ship_battleship.png");} .bs_ship3{background-image:url("battleships/ship_destroyer.png");} .bs_ship4{background-image:url("battleships/ship_submarine.png");} .bs_ship5{background-image:url("battleships/ship_patrolboat.png");} #bs_modeMenu{ position:absolute; background-color:lightgrey; left:30px; top:120px; width:590px; height:270px; border-radius:20px; outline:1px solid black; box-shadow: 0px 0px 10px 0px black, 0px 0px 10px 0px black;/* X, Y, outer extent, inner extent, color */ } .bs_modeMenu_option{ position:absolute; background-color:grey; color:black; border-radius:10px; border:1px solid black; width:250px; height:25px; left:150px; cursor:pointer; font-size:large; padding:10px; }#bs_modeMenu_option1{ top:30px; }#bs_modeMenu_option2{ top:80px; }#bs_modeMenu_option3{ top:130px; }#bs_modeMenu_option4{ top:180px; }
</style> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script>
"use strict"; $(document).ready(function(){ Startup(); StartNewGame(1); }); function Startup(){ let hhh = "<div id='bs_header'>"; hhh += "<div id='bs_title'>Kevin's Battleship</div>"; hhh += "<div id='bs_newGameButton'>New Game</div>"; hhh += "<div id='bs_help'>?</div>"; hhh += "</div>"; hhh += "<div id='bs_message'>Place your ships</div>"; hhh += "<div id='bs_fastDiv'><span>Faster</span><input type='checkbox' id='bs_fastCheckbox' checked='false'/></div>" hhh += "<div id='bs_leftBoard'>"; let style; for(let yyy=0;yyy<10;yyy++){/* left background cells */ for(let xxx=0;xxx<10;xxx++){ style = ""; style += "left:"+(30*xxx)+"px; top:"+(30*yyy)+"px;"; style += "animation-delay:"+(-100+(xxx*0.33+yyy)*1.5)+"s;"; hhh += "<div class='bs_bgCell' style='"+style+"'' id='bs_bgCell"+xxx+"_"+yyy+"'></div>"; } }hhh += "<div id='bs_cpu_ship1' class='bs_cpu_ship bs_ship1'></div>"; hhh += "<div id='bs_cpu_ship2' class='bs_cpu_ship bs_ship2'></div>"; hhh += "<div id='bs_cpu_ship3' class='bs_cpu_ship bs_ship3'></div>"; hhh += "<div id='bs_cpu_ship4' class='bs_cpu_ship bs_ship4'></div>"; hhh += "<div id='bs_cpu_ship5' class='bs_cpu_ship bs_ship5'></div>"; for(let yyy=0;yyy<10;yyy++){/* left foreground cells */ for(let xxx=0;xxx<10;xxx++){ style = ""; style += "left:"+(30*xxx)+"px; top:"+(30*yyy)+"px;"; hhh += "<div class='bs_leftCell' style='"+style+"'' id='bs_leftCell"+xxx+"_"+yyy+"'></div>"; } } hhh += "<div id='bs_leftExplosionHolder'></div>"; hhh += "</div>"; hhh += "<div id='bs_rightBoard'>"; for(let yyy=0;yyy<10;yyy++){/* right background cells */ for(let xxx=0;xxx<10;xxx++){ style = ""; style += "left:"+(30*xxx)+"px; top:"+(30*yyy)+"px;"; style += "animation-delay:"+(-100+(xxx*0.33+yyy)*1.5)+"s;"; hhh += "<div class='bs_bgCell' style='"+style+"'' id='bs_bgCell"+xxx+"_"+yyy+"'></div>"; } }hhh += "<div id='bs_you_ship1' class='bs_you_ship bs_ship1 bs_unplacedShip'></div>"; hhh += "<div id='bs_you_ship2' class='bs_you_ship bs_ship2 bs_unplacedShip'></div>"; hhh += "<div id='bs_you_ship3' class='bs_you_ship bs_ship3 bs_unplacedShip'></div>"; hhh += "<div id='bs_you_ship4' class='bs_you_ship bs_ship4 bs_unplacedShip'></div>"; hhh += "<div id='bs_you_ship5' class='bs_you_ship bs_ship5 bs_unplacedShip'></div>"; for(let yyy=0;yyy<10;yyy++){/* right foreground cells */ for(let xxx=0;xxx<10;xxx++){ style = ""; style += "left:"+(30*xxx)+"px; top:"+(30*yyy)+"px;"; hhh += "<div class='bs_rightCell' style='"+style+"'' id='bs_rightCell"+xxx+"_"+yyy+"'></div>"; } } hhh += "<div id='bs_rightExplosionHolder'></div>"; hhh += "</div>"; hhh += "<div id='bs_placingMenu'>"; hhh += "<div id='bs_placingMenu_ship1' class='bs_placingMenu_ship bs_ship1'></div>"; hhh += "<div id='bs_placingMenu_ship2' class='bs_placingMenu_ship bs_ship2'></div>"; hhh += "<div id='bs_placingMenu_ship3' class='bs_placingMenu_ship bs_ship3'></div>"; hhh += "<div id='bs_placingMenu_ship4' class='bs_placingMenu_ship bs_ship4'></div>"; hhh += "<div id='bs_placingMenu_ship5' class='bs_placingMenu_ship bs_ship5'></div>"; hhh += "<div id='bs_placingMenu_rotate'>Rotate<br><span id='bs_placingMenu_rotateSymbol'>🔄</span></div>"; hhh += "</div>"; hhh += "<div id='bs_modeMenu'>"; hhh += "<div id='bs_modeMenu_option1' class='bs_modeMenu_option'>Normal</div>"; hhh += "<div id='bs_modeMenu_option2' class='bs_modeMenu_option'>☠️Salvo (up to 5 shots)</div>"; hhh += "<div id='bs_modeMenu_option3' class='bs_modeMenu_option'>🏝️Land</div>"; hhh += "<div id='bs_modeMenu_option4' class='bs_modeMenu_option'>🏝️Land and ☠️Salvo</div>"; hhh += "</div>"; $("#battleship").html(hhh); $("#battleship").attr("oncontextmenu","return false;");/* so right-click does not open a menu */ $("#bs_newGameButton").click(NewGameButton); $("#bs_help").click(Help); $(".bs_placingMenu_ship").attr("onmousedown","ClickPlaceMenuShip(this)"); $("#bs_placingMenu_rotate").click(RotateShip); $(".bs_rightCell").hover(HoverRightCell); $(".bs_rightCell").attr("onmousedown","ClickRightCell(event,this)"); $(".bs_leftCell").attr("onmousedown","ClickLeftCell(this)"); $("#bs_modeMenu").hide(); $("#bs_modeMenu_option1").attr("onmousedown","StartNewGame(1)"); $("#bs_modeMenu_option2").attr("onmousedown","StartNewGame(2)"); $("#bs_modeMenu_option3").attr("onmousedown","StartNewGame(3)"); $("#bs_modeMenu_option4").attr("onmousedown","StartNewGame(4)"); document.getElementById("bs_fastCheckbox").checked = false; MarkDiagonalCells(); } function Help(){ let message="===========================\n"+ "How to play\n\n"+ "Try to find and destroy the enemy battleships before yours are destroyed.\n\n"+ "Right-click to rotate your ships during placement.\n\n"+ "===========================" ; alert(message); } function MarkDiagonalCells(){ let pos={x:0,y:0}; GetRightCell(pos.x,pos.y).addClass("bs_isDiagonal"); while(true){ pos.x+=2; if(pos.x>9){pos.x-=9+2;pos.y++;} if(pos.y>9)break; GetRightCell(pos.x,pos.y).addClass("bs_isDiagonal"); } } const shipNames = [ "ERROR", "Carrier", "Battleship", "Destroyer", "Submarine", "Patrol Boat" ]; const shipLengths = [99,5,4,3,3,2]; const xLetters = ["A","B","C","D","E","F","G","H","I","J"]; let gameMode = 1; let gameStep = 1; let shipSelected = 1; let youSalvo = 5; let cpuSalvo = 5; let ammo = 1; function NewGameButton(){ clearTimeout(timer_turnDelay); $("#bs_modeMenu").show(); $("#bs_placingMenu").hide(); BoardBlocks(true,true); BoardBorders(false,false); HideExplosions(); ShowShips(false,false); Message("Choose a game"); gameStep=0; } function StartNewGame(mode){ $("#bs_modeMenu").hide(); /* reset everything */ $(".bs_rightCell").add(".bs_leftCell") .removeClass("bs_has_ship") .removeClass("bs_has_ship1") .removeClass("bs_has_ship2") .removeClass("bs_has_ship3") .removeClass("bs_has_ship4") .removeClass("bs_has_ship5") .removeClass("bs_has_peg") .removeClass("bs_has_red") .removeClass("bs_has_white") .removeClass("bs_has_land"); $(".bs_placingMenu_ship").removeClass("bs_placingMenu_placedShip"); $(".bs_you_ship").removeClass("bs_placedShip").removeClass("bs_heldShip").addClass("bs_unplacedShip"); HoldShip(1); $("#bs_placingMenu").show(); BoardBlocks(true,false); BoardBorders(false,true); ShowShips(false,true); gameStep=1; gameMode=mode; youSalvo=5; cpuSalvo=5; /* 1 normal */ /* 2 salvo */ /* 3 islands normal */ /* 4 islands salvo */ if(gameMode==3 || gameMode==4){/* islands */ MakeLand(); } } function MakeLand(){ for(let i=0;i<30;i++){ while(true){ let pos; let cell; if(Math.random()<0.2){/* random */ pos = { x: Math.floor(Math.random()*10), y: Math.floor(Math.random()*10) }; cell = GetLeftCell(pos.x,pos.y); if(cell.hasClass("bs_has_land"))continue; }else{/* append to current land */ let lands = $("#bs_leftBoard").children(".bs_has_land"); if(lands.length==0)continue; let randomLand = lands.eq(Math.floor(Math.random()*lands.length)); let neighs = GetLeftNeighbors_Perpendicular(randomLand); neighs = neighs.not(".bs_has_land"); if(neighs.length==0)continue; cell = neighs.eq(Math.floor(Math.random()*neighs.length)); } /* apply one land */ cell.addClass("bs_has_land"); pos = GetCoords(cell);GetRightCell(pos.x,pos.y).addClass("bs_has_land"); break; } } } function GetLeftNeighbors_Perpendicular(cell){ let neighs = $(); let c = GetCoords(cell); if(c.x-1 < 0){}else neighs = neighs.add(GetLeftCell(c.x-1,c.y)); if(c.x+1 > 9){}else neighs = neighs.add(GetLeftCell(c.x+1,c.y)); if(c.y-1 < 0){}else neighs = neighs.add(GetLeftCell(c.x,c.y-1)); if(c.y+1 > 9){}else neighs = neighs.add(GetLeftCell(c.x,c.y+1)); return neighs; } function HoverRightCell(){ if(gameStep == 1){/* placing your ships */ let xs = $(this).css("left"); let ys = $(this).css("top"); $(".bs_heldShip").css("left",xs); $(".bs_heldShip").css("top",ys); } } function ClickPlaceMenuShip(element){ /* initiate switch ship */ let id = $(element).attr("id"); let shipDigit = id[id.length-1]; /* unselect current held ship */ $(".bs_heldShip").removeClass("bs_heldShip"); HoldShip(shipDigit); } function HoldShip(id){ shipSelected = id; $("#bs_you_ship"+shipSelected) .removeClass("bs_placedShip") .addClass("bs_unplacedShip") .addClass("bs_heldShip"); /* un-dim one on menu */ $("#bs_placingMenu_ship"+shipSelected).removeClass("bs_placingMenu_placedShip"); /* remove from board */ $(".bs_has_ship"+shipSelected) .removeClass("bs_has_ship") .removeClass("bs_has_ship"+shipSelected); /* message */ Message("Place your "+shipNames[id]); } const messageAnim="bs_messageFlash 0.5s linear 0s 1 normal"; function Message(m){ $("#bs_message").html(m); /* reset animation */ let e=document.getElementById("bs_message"); e.style.animation="none"; e.offsetHeight;/* trigger a "reflow" */ e.style.animation=messageAnim; } function BoardBlocks(left,right){ left ? $("#bs_leftBoard").addClass("bs_board_block") : $("#bs_leftBoard").removeClass("bs_board_block"); right ? $("#bs_rightBoard").addClass("bs_board_block") : $("#bs_rightBoard").removeClass("bs_board_block"); } function BoardBorders(left,right){ left ? $("#bs_leftBoard").addClass("bs_board_border") : $("#bs_leftBoard").removeClass("bs_board_border"); right ? $("#bs_rightBoard").addClass("bs_board_border") : $("#bs_rightBoard").removeClass("bs_board_border"); } function HideExplosions(){ $("#bs_rightExplosionHolder").hide(); $("#bs_leftExplosionHolder").hide(); } function ShowShips(left,right){ left ? $(".bs_cpu_ship").show() : $(".bs_cpu_ship").hide(); right ? $(".bs_you_ship").css("opacity","") : $(".bs_you_ship").css("opacity",0.5); } function PlaceShip(){ $(".bs_heldShip") .removeClass("bs_heldShip") .removeClass("bs_unplacedShip") .addClass("bs_placedShip"); /* dim one on menu */ $("#bs_placingMenu_ship"+shipSelected).addClass("bs_placingMenu_placedShip"); /* cycle to next ship */ if($("#bs_you_ship1").hasClass("bs_placedShip")==false)HoldShip(1); else if($("#bs_you_ship2").hasClass("bs_placedShip")==false)HoldShip(2); else if($("#bs_you_ship3").hasClass("bs_placedShip")==false)HoldShip(3); else if($("#bs_you_ship4").hasClass("bs_placedShip")==false)HoldShip(4); else if($("#bs_you_ship5").hasClass("bs_placedShip")==false)HoldShip(5); else{ /* finished placing ships */ $("#bs_placingMenu").hide(); CpuPlaceShips(); YourTurn(); } } function ClickRightCell(event,cell){ if(gameStep == 1){ /* placing ships */ if(event.button == 2){ RotateShip(); }else if(event.button == 0){ TryPlaceShip(cell); } } } function RotateShip(){ $(".bs_heldShip").toggleClass("bs_shipRotated"); } function TryPlaceShip(cell){ let pos = GetCoords(cell); let shipLength = shipLengths[parseInt(shipSelected)]; let xmax = 1; let ymax = 1; if($(".bs_heldShip").hasClass("bs_shipRotated")){ ymax=shipLength; }else{ xmax=shipLength; } /* check if able to place */ for(let x=0;x<xmax;x++){ for(let y=0;y<ymax;y++){ if(pos.x+x > 9)return false; if(pos.y-y < 0)return false; let otherCell = GetRightCell(pos.x+x,pos.y-y); if(otherCell.hasClass("bs_has_ship"))return false; if(otherCell.hasClass("bs_has_land"))return false; } } /* able to place, go ahead */ for(let x=0;x<xmax;x++){ for(let y=0;y<ymax;y++){ GetRightCell(pos.x+x,pos.y-y) .addClass("bs_has_ship") .addClass("bs_has_ship"+shipSelected); } } PlaceShip(); return true; } function GetRightCell(x,y){ return $("#bs_rightCell"+x+"_"+y); } function GetLeftCell(x,y){ return $("#bs_leftCell"+x+"_"+y); } function GetCoords(element){ let id = $(element).attr("id"); let coordString = id.split("Cell")[1]; let bits = coordString.split("_"); return {x:parseInt(bits[0]), y:parseInt(bits[1])}; } let timer_turnDelay; function ClickLeftCell(cell){ if(gameStep==2){/* shooting enemy */ if($(cell).hasClass("bs_has_peg")==false && $(cell).hasClass("bs_has_land")==false){ if($(cell).hasClass("bs_has_ship")==true){ $(cell).addClass("bs_has_peg").addClass("bs_has_red"); Message(GetHitMessage("You",cell)); Explosion(cell); if(IsAllShipsDestroyed(true)==true){ /* you win */ gameStep=4; timer_turnDelay=setTimeout(YouWin,Speed(3000,2000)); return; } }else{ $(cell).addClass("bs_has_peg").addClass("bs_has_white"); Message(GetMissMessage("You",cell)); WaterExplosion(cell); /*$("#bs_leftExplosionHolder").hide(); */ } ammo--; if(gameMode==2 || gameMode==4){ Message($("#bs_message").text()+".<br>"+ammo+" shots left."); } if(ammo<=0){ gameStep=3; timer_turnDelay=setTimeout(CpuTurn,Speed(3000,2000)); BoardBlocks(true,true); BoardBorders(false,false); } } } } function YouWin(){ gameStep=4; let yourShots = $("#bs_leftBoard").children(".bs_has_peg").length; let cpuShots = $("#bs_rightBoard").children(".bs_has_peg").length; Message("You win!<br>Shots fired (You "+yourShots+") (CPU "+cpuShots+")"); BoardBlocks(true,true); BoardBorders(false,false); ShowShips(true,true); HideExplosions(); } function YouLose(){ gameStep=4; let yourShots = $("#bs_leftBoard").children(".bs_has_peg").length; let cpuShots = $("#bs_rightBoard").children(".bs_has_peg").length; Message("You lose!<br>Shots fired (You "+yourShots+") (CPU "+cpuShots+")"); BoardBlocks(true,true); BoardBorders(false,false); ShowShips(true,true); HideExplosions(); } function GetHitMessage(playerName,cell){ let shipId = 0; if($(cell).hasClass("bs_has_ship1")==true)shipId=1; else if($(cell).hasClass("bs_has_ship2")==true)shipId=2; else if($(cell).hasClass("bs_has_ship3")==true)shipId=3; else if($(cell).hasClass("bs_has_ship4")==true)shipId=4; else if($(cell).hasClass("bs_has_ship5")==true)shipId=5; let hitOrSunk = IsShipAlive(playerName=="You",shipId) ? "HIT" : "SUNK"; if(hitOrSunk == "SUNK"){ playerName=="You" ? cpuSalvo-- : youSalvo--;/* reduce salvo */ } let shipName = shipNames[shipId]; let pos = GetCoords(cell); let xName = xLetters[pos.x]; let yName = pos.y+1; return playerName+" "+hitOrSunk+" "+shipName+" at "+xName+yName; } function GetMissMessage(playerName,cell){ let pos = GetCoords(cell); let xName = xLetters[pos.x]; let yName = pos.y+1; return playerName+" Miss at "+xName+yName; } function IsShipAlive(checkLeft,shipId){ let board = checkLeft ? $("#bs_leftBoard") : $("#bs_rightBoard"); let remains = board.children(".bs_has_ship"+shipId).not(".bs_has_peg").length; return remains>0; } function IsAllShipsDestroyed(checkLeft){ let board = checkLeft ? $("#bs_leftBoard") : $("#bs_rightBoard"); let remains = board.children(".bs_has_ship").not(".bs_has_peg").length; return remains==0; } function YourTurn(){ gameStep=2; HideExplosions(); BoardBlocks(false,true); BoardBorders(true,false); ShowShips(false,true); if(gameMode==2 || gameMode==4){/*salvo */ ammo=youSalvo; Message("Shoot the enemy.<br>"+ammo+" shots left."); }else{ ammo=1; Message("Shoot the enemy."); } } function WaterExplosion(cell){ Explosion2(cell,true); } function Explosion(cell){ Explosion2(cell,false); } function Explosion2(cell,isWater){ let html=""; let style=""; let size=300.0; let dist=0.0; let angle=Math.random()*360.0; let rrr=0.0; let xxx=0.0; let yyy=0.0; for(let i=0;i<30;i++){ angle+=72.0; angle+=(Math.random()*15.0)-7.5; rrr=angle*Math.PI/180.0; xxx=Math.sin(rrr)*dist; yyy=Math.cos(rrr)*dist; /*xxx=shipX+xxx; */ /*yyy=shipY+yyy; */ xxx-=5;/* for the initial explosion size */ yyy-=5; style="left:"+xxx+"px;bottom:"+yyy+"px;"; style+="transform:scale("+size+"%,"+size+"%);"; style+="animation-delay:"+((i/30)*0.5)+"s;"; if(isWater){ html+="<div class='bs_waterExplosion' style='"+style+"'></div>"; }else{ html+="<div class='bs_explosion' style='"+style+"'></div>"; } size-=(300-40)/30; dist+=( 70/30); } let holder; if($(cell).hasClass("bs_leftCell")){ holder = $("#bs_leftExplosionHolder"); }else holder = $("#bs_rightExplosionHolder"); let pos = GetCoords(cell); holder.css("left",(pos.x*30+15)+"px").css("top",(pos.y*30+15)+"px").show(); holder.html(html); } /* ################################################# */ function CpuPlaceShips(){ while(true){if(Cpu_TryPlaceShip(1)==true)break;} while(true){if(Cpu_TryPlaceShip(2)==true)break;} while(true){if(Cpu_TryPlaceShip(3)==true)break;} while(true){if(Cpu_TryPlaceShip(4)==true)break;} while(true){if(Cpu_TryPlaceShip(5)==true)break;} } function Cpu_TryPlaceShip(id){ let pos = { x: Math.floor(Math.random()*10), y: Math.floor(Math.random()*10) }; $("#bs_cpu_ship"+id) .css("left",(pos.x*30)+"px") .css("top",(pos.y*30)+"px"); let shipLength = shipLengths[id]; let xmax = 1; let ymax = 1; if(Math.random()>0.5){/* decide rotate */ ymax=shipLength; $("#bs_cpu_ship"+id).addClass("bs_shipRotated"); }else{ xmax=shipLength; $("#bs_cpu_ship"+id).removeClass("bs_shipRotated"); } /* check if able to place */ for(let x=0;x<xmax;x++){ for(let y=0;y<ymax;y++){ if(pos.x+x > 9)return false; if(pos.y-y < 0)return false; let otherCell = GetLeftCell(pos.x+x,pos.y-y); if(otherCell.hasClass("bs_has_ship"))return false; if(otherCell.hasClass("bs_has_land"))return false; } } /* able to place, go ahead */ for(let x=0;x<xmax;x++){ for(let y=0;y<ymax;y++){ GetLeftCell(pos.x+x,pos.y-y) .addClass("bs_has_ship") .addClass("bs_has_ship"+id); } }return true; } function CpuTurn(){ gameStep=3; $("#bs_leftExplosionHolder").hide(); BoardBlocks(true,true); BoardBorders(false,true); ShowShips(false,false); Message("Please wait..."); if(gameMode==2 || gameMode==4){/*salvo */ ammo=cpuSalvo; }else{ ammo=1; } timer_turnDelay=setTimeout(Cpu_ShootSmart,Speed(2000,100)); } function Cpu_ShootRandom(){ let pos; let cell; while(true){ pos = { x: Math.floor(Math.random()*10), y: Math.floor(Math.random()*10) }; cell = $("#bs_rightCell"+pos.x+"_"+pos.y); if(cell.hasClass("bs_has_peg")==false && $(cell).hasClass("bs_has_land")==false)break; } Cpu_ActuallyShoot(cell); } function Cpu_ActuallyShoot(cell){ if($(cell).hasClass("bs_has_peg")){ console.log("ERROR cpu trying to shoot pegged cell");Message("ERROR"); return; } if($(cell).hasClass("bs_has_ship")==true){ $(cell).addClass("bs_has_peg").addClass("bs_has_red"); Message(GetHitMessage("CPU",cell)); Explosion(cell); if(IsAllShipsDestroyed(false)==true){ /* you lose */ gameStep=4; timer_turnDelay=setTimeout(YouLose,Speed(3000,2000)); return; } }else{ $(cell).addClass("bs_has_peg").addClass("bs_has_white"); Message(GetMissMessage("CPU",cell)); WaterExplosion(cell); } ammo--; if(gameMode==2 || gameMode==4){ Message($("#bs_message").text()+".<br>"+ammo+" shots left."); } if(ammo<=0){ gameStep=0; timer_turnDelay=setTimeout(YourTurn,Speed(2500,1000)); }else{ timer_turnDelay=setTimeout(Cpu_ShootSmart,Speed(2500,900)); } } function Speed(normal,fast){ return document.getElementById("bs_fastCheckbox").checked ? fast : normal; } /*---------------------------------------- */ function Cpu_ShootSmart(){ /* unfinished ships? followup on them */ for(let ship=1;ship<shipLengths.length;ship++){ let redPegs = $("#bs_rightBoard").children(".bs_has_ship"+ship).filter(".bs_has_red"); if(redPegs.length>0 && redPegs.length<shipLengths[ship]){ /*Cpu_ShootRandom(); */ Cpu_Followup(ship,redPegs); return; } } /* hunt remaining ships */ for(let ship=1;ship<shipLengths.length;ship++){ let redPegs = $("#bs_rightBoard").children(".bs_has_ship"+ship).filter(".bs_has_red"); if(redPegs.length==0){ /*Cpu_ShootRandom(); */ Cpu_Hunt(ship); return; } } console.log("ERROR cpu cannot shoot smart");Message("ERROR"); } function Cpu_Followup(ship,redPegs){ let cell; let cells; if(redPegs.length==1){ cells = GetRightNeighbors_Perpendicular(redPegs); cells = cells.not(".bs_has_land").not(".bs_has_peg"); if(cells.length==0){console.log("ERROR no neighs");Message("ERROR");return;} cell = cells.eq(Math.floor(Math.random()*cells.length)); Cpu_ActuallyShoot(cell); return; /* dont guess direction where boat cannot fit? */ }else{ if(GetCoords(redPegs.eq(0)).x == GetCoords(redPegs.eq(1)).x){ /* is vertical */ /* shoot from top to bottom */ cell = TopMostCell(redPegs); while(true){ cell = GetNeighbor(cell,0,1); if(cell == null || cell.hasClass("bs_has_land") || cell.hasClass("bs_has_white")){ /* other direction */ cell = TopMostCell(redPegs); cell = GetNeighbor(cell,0,-1); Cpu_ActuallyShoot(cell); return; }else if(cell.hasClass("bs_has_red")){ continue; }else{ Cpu_ActuallyShoot(cell); return; } } }else{ /* is horizontal */ /* shoot from left to right */ cell = LeftMostCell(redPegs); while(true){ cell = GetNeighbor(cell,1,0); if(cell == null || cell.hasClass("bs_has_land") || cell.hasClass("bs_has_white")){ /* other direction */ cell = LeftMostCell(redPegs); cell = GetNeighbor(cell,-1,0); Cpu_ActuallyShoot(cell); return; }else if(cell.hasClass("bs_has_red")){ continue; }else{ Cpu_ActuallyShoot(cell); return; } } } } } function Cpu_Hunt(ship){ /* look for all possible horizontal positions */ /* add +1 score to all cells involved */ /* then look for all possible vertical positions */ /* add +1 score to all cells involved */ /* higher score cells are better guesses */ let cellScores = NewCellScoresArray(10*10); /* score each cell */ let x; let y; for(x=0;x<10;x++){ for(y=0;y<10;y++){ ScoreAreaIfShipFits_Horizontal(shipLengths[ship],x,y); ScoreAreaIfShipFits_Vertical(shipLengths[ship],x,y); } } /* find best scores */ let maxScore = GetMaxScore(cellScores); let cell; let cells = $(); for(x=0;x<cellScores.length;x++){ if(cellScores[x]==maxScore){ let pos = ScoreIndexToCoords(x); cells = cells.add(GetRightCell(pos.x,pos.y)); } } /* prioritize diagonals */ let diagCells = cells.filter(".bs_isDiagonal"); if(diagCells.length>0){ cell = diagCells.eq(Math.floor(Math.random()*diagCells.length)); Cpu_ActuallyShoot(cell); return; }else{ cell = cells.eq(Math.floor(Math.random()*cells.length)); Cpu_ActuallyShoot(cell); return; } console.log("ERROR cpu cannot hunt");Message("ERROR"); function NewCellScoresArray(size){ let a = new Array(size); for(let i=0;i<a.length;i++){ a[i]=0; }return a; } function GetMaxScore(a){ let max = -999; for(let i=0;i<a.length;i++){ if(a[i]>max)max=a[i]; }return max; } function CoordsToScoreIndex(x,y){ return x + y*10; } function ScoreIndexToCoords(index){ let y = Math.floor(index/10); let x = index - (y*10); return {x:x,y:y}; } function ScoreAreaIfShipFits_Horizontal(shipLength,ox,oy){ let x; for(x=0;x<shipLength;x++){ if(ox + x > 9)return; let cell = GetRightCell(ox + x, oy); if(cell.hasClass("bs_has_land") || cell.hasClass("bs_has_peg")){ return; } } for(x=0;x<shipLength;x++){ cellScores[CoordsToScoreIndex(ox+x,oy)]++; } } function ScoreAreaIfShipFits_Vertical(shipLength,ox,oy){ let y; for(y=0;y<shipLength;y++){ if(oy + y > 9)return; let cell = GetRightCell(ox, oy + y); if(cell.hasClass("bs_has_land") || cell.hasClass("bs_has_peg")){ return; } } for(y=0;y<shipLength;y++){ cellScores[CoordsToScoreIndex(ox,oy+y)]++; } } } function GetRightNeighbors_Perpendicular(cell){ let neighs = $(); let c = GetCoords(cell); if(c.x-1 < 0){}else neighs = neighs.add(GetRightCell(c.x-1,c.y)); if(c.x+1 > 9){}else neighs = neighs.add(GetRightCell(c.x+1,c.y)); if(c.y-1 < 0){}else neighs = neighs.add(GetRightCell(c.x,c.y-1)); if(c.y+1 > 9){}else neighs = neighs.add(GetRightCell(c.x,c.y+1)); return neighs; } function GetNeighbor(cell,xoffset,yoffset){ let pos = GetCoords(cell); if(pos.x+xoffset <0 || pos.x+xoffset >9)return null; if(pos.y+yoffset <0 || pos.y+yoffset >9)return null; return GetRightCell(pos.x+xoffset,pos.y+yoffset); } function TopMostCell(cells){ let minId = -1; let minValue = 999; for(let i=0;i<cells.length;i++){ let pos = GetCoords(cells.eq(i)); if(pos.y < minValue){ minValue = pos.y; minId = i; } }return cells.eq(minId); } function LeftMostCell(cells){ let minId = -1; let minValue = 999; for(let i=0;i<cells.length;i++){ let pos = GetCoords(cells.eq(i)); if(pos.x < minValue){ minValue = pos.x; minId = i; } }return cells.eq(minId); }
</script> <style>
body{ background-color:rgb(78, 56, 87); }
</style> </head> <body> <center> <div id="battleship"></div> </center> </body> </html>
0000