<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Kevin's Stratego</title> <style>
/* ai ideas -- attacking movers is safer after killing your 7-8-9-10 -- attacking dormants is safer after killing your bombs */ #stratego{ position:relative; background-color:lightgrey; font-size:1em; width:530px; height:620px; border:1px solid black; text-align:left; user-select:none; font-family:sans-serif; overflow:hidden; } #sgo_header{ position:absolute; width:100%; height:30px; left:0; top:0; overflow:hidden; border:1px solid black; background-color: grey; } #sgo_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; } #sgo_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; } #sgo_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; } #sgo_message{ position:absolute; left:100px; top:40px; width:calc(100% - 200px - 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: sgo_messageFlash 0.5s linear 0s 1 normal; }@keyframes sgo_messageFlash{ from{background-color:rgb(123, 255, 123);} to{background-color:rgb(22, 48, 22);} } #sgo_fastDiv{ position:absolute; right:10px; top:50px; } #sgo_board{ position:absolute; width:calc(50px * 10); height:calc(50px * 10); top:100px; left:10px; background-image:linear-gradient(forestgreen 0%,darkkhaki 50%,forestgreen 100%); border:5px inset grey; cursor:pointer; } .sgo_cell{ position:absolute; width:48px; height:48px; border:1px solid darkolivegreen; } .sgo_lake{ position:absolute; width:98px; height:98px; top:calc(50px * 4); border:1px solid darkolivegreen; background-image:radial-gradient(darkslategrey 10%,dimgrey 65%,darkkhaki 82%); }#sgo_lakeLeft{ left:calc(50px * 2); }#sgo_lakeRight{ left:calc(50px * 6); } .sgo_piece{ position:absolute; width:40px; height:40px; margin: 5px 0px 0px 5px; background-image:linear-gradient(darkgrey 0%,grey 100%); border-radius: 99px 99px 0px 0px; color:white; text-align: center; font-weight: bold; font-size: 1.7em; box-shadow: 0px -3px 5px 0px black; } .sgo_pieceRed{ background-image:linear-gradient(firebrick 0%,maroon 100%); }.sgo_pieceBlue{ background-image:linear-gradient(darkslategrey 0%,midnightblue 100%) } .sgo_piece2{ color:lightskyblue; }.sgo_piece3{ color:violet; }.sgo_pieceBomb{ color:red; }.sgo_pieceFlag{ color:darkgrey; }.sgo_pieceRed.sgo_secret{/* secret identity */ font-size:0.0em; } #sgo_selector{ position:absolute; pointer-events: none; }.sgo_selectorChoose{ width:42px; height:42px; border:4px dashed yellow; box-shadow: 0px 0px 10px 0px black; animation:sgo_selectorChoose 0.2s linear 0s infinite alternate; }@keyframes sgo_selectorChoose{ 0%{opacity:1;} 60%{opacity:1;} 61%{opacity:0;} 100%{opacity:0;} }.sgo_selectorBattleRed,.sgo_selectorBattleBlue{ width:100px; height:100px; background-image:radial-gradient(rgba(0,0,0,0) 0%,white 100%); box-shadow: 0px 0px 10px 0px black; border-radius: 999px; margin: -25px 0px 0px -25px; animation:sgo_selectorBattle 0.2s linear 0s infinite alternate; }.sgo_selectorBattleRed{ background-image:radial-gradient(rgba(0,0,0,0) 60%,red 61%); }.sgo_selectorBattleBlue{ background-image:radial-gradient(rgba(0,0,0,0) 60%,deepskyblue 61%); }@keyframes sgo_selectorBattle{ 0%{opacity:1;} 60%{opacity:1;} 61%{opacity:0;} 100%{opacity:0;} } #sgo_startButton{ position:absolute; width:150px; height:30px; right:25px; top:230px; font-size:1.2em; color:grey; text-align: center; background-color:lightgrey; border:6px double grey; border-radius:99px; box-shadow: 0px 0px 10px 0px black; cursor:pointer; animation:sgo_startButton 0.7s ease-out 0s infinite alternate; }@keyframes sgo_startButton{ from{right:25px;} to{right:35px;} } .sgo_layoutButton{ position:absolute; width:200px; height:25px; left:35px; color:black; background-color:lightsteelblue; border:6px double grey; border-radius:99px; box-shadow: 0px 0px 10px 0px black; cursor:pointer; }#sgo_layoutButton1{ top:130px; }#sgo_layoutButton2{ top:170px; }#sgo_layoutButton3{ top:210px; }#sgo_layoutButton4{ top:250px; }
</style> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script>
"use strict"; const ICON_BOMB = "💣"; const ICON_FLAG = "🚩"; $(document).ready(function(){ Startup(); OnNewGame(); }); function Startup(){ let hhh = "<div id='sgo_header'>"; hhh += "<div id='sgo_title'>Kevin's Stratego</div>"; hhh += "<div id='sgo_newGameButton'>New Game</div>"; hhh += "<div id='sgo_help'>?</div>"; hhh += "</div>"; hhh += "<div id='sgo_message'>my message</div>"; hhh += "<div id='sgo_fastDiv'><span>Faster</span><input type='checkbox' id='sgo_fastCheckbox' checked='false'/></div>"; hhh += "<div id='sgo_board'>"; let tIdent; let tClass; let tStyle; let tContent; /* board cells */ for(let yyy=0;yyy<10;yyy++){ for(let xxx=0;xxx<10;xxx++){ tIdent = "sgo_cell"+xxx+"_"+yyy; tClass = "sgo_cell"; tStyle = ""; tStyle += "left:"+(50*xxx)+"px; top:"+(50*yyy)+"px;"; hhh += "<div class='"+tClass+"' style='"+tStyle+"' id='"+tIdent+"'></div>"; } } /* lakes */ tIdent = "sgo_lakeLeft"; tClass = "sgo_lake"; tStyle = ""; /*tStyle = "left:"+(50*2)+"px; top:"+(50*4)+"px;"; */ hhh += "<div class='"+tClass+"' style='"+tStyle+"' id='"+tIdent+"'></div>"; tIdent = "sgo_lakeRight"; hhh += "<div class='"+tClass+"' style='"+tStyle+"' id='"+tIdent+"'></div>"; /* pieces */ for(let i=0;i<40;i++){ tIdent = "sgo_pieceRed"+i; tClass = "sgo_piece sgo_pieceRed"; tContent = "A"; hhh += "<div class='"+tClass+"' id='"+tIdent+"'>"+tContent+"</div>"; tIdent = "sgo_pieceBlue"+i; tClass = "sgo_piece sgo_pieceBlue"; hhh += "<div class='"+tClass+"' id='"+tIdent+"'>"+tContent+"</div>"; } /* selector */ hhh += "<div id='sgo_selector'></div>"; hhh += "</div>";/* close board */ hhh += "<div id='sgo_startButton'>Start Game</div>"; hhh += "<div id='sgo_layoutButton1' class='sgo_layoutButton'></div>"; hhh += "<div id='sgo_layoutButton2' class='sgo_layoutButton'></div>"; hhh += "<div id='sgo_layoutButton3' class='sgo_layoutButton'></div>"; hhh += "<div id='sgo_layoutButton4' class='sgo_layoutButton'></div>"; $("#stratego").html(hhh); $("#stratego").attr("oncontextmenu","return false;");/* so right-click does not open a menu */ $("#sgo_newGameButton").click(OnNewGame); $("#sgo_help").click(Help); document.getElementById("sgo_fastCheckbox").checked = false; $("#sgo_startButton").click(OnStartGame); $("#sgo_layoutButton1").click(ArrangeBluePiecesRandom).text("📐 Random"); $("#sgo_layoutButton2").click(ArrangeBluePieces_BombCenter).text("📐 Bomb Center"); $("#sgo_layoutButton3").click(ArrangeBluePieces_BombSides).text("📐 Bomb Sides"); $("#sgo_layoutButton4").click(ArrangeBluePieces_OpenLeft).text("📐 Open Left"); $(".sgo_cell").attr("onmousedown","OnClickCell(event,this)"); $(".sgo_piece").attr("onmousedown","OnClickPiece(event,this)"); RemoveLakeCells(); AssignRanksToPieces($(".sgo_pieceRed")); AssignRanksToPieces($(".sgo_pieceBlue")); function Help(){ let message="===========================\n"+ "HOW TO PLAY\n\n"+ "Find the enemy flag!\n"+ "Bigger pieces kill smaller ones.\n\n"+ "[bomb] cannot move.\n"+ "[flag] cannot move.\n\n"+ "[bomb] kills any attacker, except [3].\n"+ "[1] can attack [10].\n"+ "[2] can move far.\n"+ "[3] kills [bomb].\n\n"+ "===========================" ; alert(message); } function RemoveLakeCells(){ $("#sgo_cell2_4").remove(); $("#sgo_cell2_5").remove(); $("#sgo_cell3_4").remove(); $("#sgo_cell3_5").remove(); $("#sgo_cell6_4").remove(); $("#sgo_cell6_5").remove(); $("#sgo_cell7_4").remove(); $("#sgo_cell7_5").remove(); } function AssignRanksToPieces(elements){ if(elements.length != 40) console.log("error for rankings"); for(let i=0;i<40;i++) elements.eq(i).addClass("sgo_dormant").addClass("sgo_secret"); let tIndex=0; for(let i=0;i< 1 ;i++){ elements.eq(tIndex).text("1").addClass("sgo_piece1"); tIndex++; } for(let i=0;i< 8 ;i++){ elements.eq(tIndex).text("2").addClass("sgo_piece2"); tIndex++; } for(let i=0;i< 5 ;i++){ elements.eq(tIndex).text("3").addClass("sgo_piece3"); tIndex++; } for(let i=0;i< 4 ;i++){ elements.eq(tIndex).text("4").addClass("sgo_piece4"); tIndex++; } for(let i=0;i< 4 ;i++){ elements.eq(tIndex).text("5").addClass("sgo_piece5"); tIndex++; } for(let i=0;i< 4 ;i++){ elements.eq(tIndex).text("6").addClass("sgo_piece6"); tIndex++; } for(let i=0;i< 3 ;i++){ elements.eq(tIndex).text("7").addClass("sgo_piece7"); tIndex++; } for(let i=0;i< 2 ;i++){ elements.eq(tIndex).text("8").addClass("sgo_piece8"); tIndex++; } for(let i=0;i< 1 ;i++){ elements.eq(tIndex).text("9").addClass("sgo_piece9"); tIndex++; } for(let i=0;i< 1 ;i++){ elements.eq(tIndex).text("10").addClass("sgo_piece10"); tIndex++; } for(let i=0;i< 6 ;i++){ elements.eq(tIndex).text(ICON_BOMB).addClass("sgo_pieceBomb"); tIndex++; } for(let i=0;i< 1 ;i++){ elements.eq(tIndex).text(ICON_FLAG).addClass("sgo_pieceFlag"); tIndex++; } } } let selectedPiece = null; let attackedPiece = null; let prevBattleResult = ""; let gameState = ""; let gameTimer = null; const moveSpeed = 2000; const moveSpeedFast = 800; function GetSpeed(){ return document.getElementById("sgo_fastCheckbox").checked ? moveSpeedFast : moveSpeed; } function OnNewGame(){ clearTimeout(gameTimer); gameTimer=null; attackedPiece = null; prevBattleResult = ""; if(selectedPiece!=null){ selectedPiece.stop(true,true); selectedPiece=null; } Startup(); ArrangeRedPiecesRandom(); ArrangeBluePiecesRandom(); gameState = "arrange"; Message("Arrange your pieces!<br>Click pieces to swap them."); SetSelectorMode(null); $("#sgo_startButton").show(); $(".sgo_layoutButton").show(); } function OnStartGame(){ gameState = "your turn"; Message("Your turn. Move a piece."); SetSelectorMode(null); $("#sgo_startButton").hide(); $(".sgo_layoutButton").hide(); } function OnClickPiece(event,piece){ piece = $(piece); if(gameTimer!=null) return; if(gameState=="arrange"){ if(piece.hasClass("sgo_pieceBlue")){ /* select a piece to move */ if(selectedPiece==null){ selectedPiece = piece; SetSelectorMode("choose"); MoveElementToElement($("#sgo_selector"),piece); /* deselect */ }else if(selectedPiece==piece){ selectedPiece = null; SetSelectorMode(null); /* swap and deselect */ }else{ let tCell = GetCellAtPiece(selectedPiece); MoveElementToElement(selectedPiece,piece); MoveElementToElement(piece,tCell); selectedPiece = null; SetSelectorMode(null); } } }else if(gameState=="your turn"){ /* selecting a blue piece */ if(piece.hasClass("sgo_pieceBlue")){ TrySelectBluePiece(piece); /* attacking a red piece */ }else if(piece.hasClass("sgo_pieceRed")){ if(selectedPiece != null){ TryAttackRedPiece(piece); } } } } function OnClickCell(event,cell){ cell = $(cell); if(gameTimer!=null) return; if(gameState=="arrange"){ selectedPiece = null; SetSelectorMode(null); }else if(gameState=="your turn"){ /* move selectedPiece to cell */ if(selectedPiece != null){ TryMoveBluePieceToCell(cell); } } } function TrySelectBluePiece(piece){ if(piece == null){ selectedPiece = null; }else if(piece.text()==ICON_BOMB || piece.text()==ICON_FLAG){ selectedPiece = null; }else{ selectedPiece = piece; SetSelectorMode("choose"); MoveElementToElement($("#sgo_selector"),piece); } } function TryAttackRedPiece(otherPiece){ if(MovePathIsValid(GetCellAtPiece(otherPiece)) == false) return; attackedPiece = otherPiece; prevBattleResult = GetBattleResult(selectedPiece,otherPiece); /* ai data */ selectedPiece.removeClass("sgo_dormant"); selectedPiece.removeClass("sgo_secret"); /* message and selector */ MoveElementToElement($("#sgo_selector"),otherPiece); if(prevBattleResult=="win flag"){ Message("Found enemy flag! You win!"); SetSelectorMode("battleBlue"); }else if(prevBattleResult.includes("win")){ Message("Your ["+selectedPiece.text()+"] defeated enemy ["+(otherPiece.text()==ICON_BOMB?"bomb":otherPiece.text())+"]."); SetSelectorMode("battleBlue"); }else if(prevBattleResult.includes("lose")){ Message("Enemy ["+(otherPiece.text()==ICON_BOMB?"bomb":otherPiece.text())+"] defeated your ["+selectedPiece.text()+"]."); SetSelectorMode("battleRed"); }else if(prevBattleResult=="tie"){ Message("Both ["+selectedPiece.text()+"] pieces were defeated."); SetSelectorMode("battleBlue"); } /* animate towards */ let timeForTimeout = GetSpeed()-(GetSpeed()/(GetDistance()+0.6)); AnimateElementToElement(selectedPiece,otherPiece); selectedPiece.css("z-index",1); otherPiece.css("z-index",0); otherPiece.removeClass("sgo_secret");/* show enemy temporarily */ gameTimer = setTimeout(function(){ selectedPiece.stop(true,false); gameTimer = setTimeout(function(){/* delay */ BattleFollowup(); TurnFollowup(); },GetSpeed()) },timeForTimeout);/* stop early */ function GetDistance(){ let pieceCoords = GetCellCoords( GetCellAtPiece(selectedPiece) ); let destCoords = GetCellCoords( GetCellAtPiece(attackedPiece) ); let dad = GetDirectionAndDistanceBetweenCoords(pieceCoords,destCoords); return dad.distance; } } function TryMoveBluePieceToCell(cell){ if(GetPieceAtCell(cell) != null) return; if(MovePathIsValid(cell) == false) return; /* ai data */ selectedPiece.removeClass("sgo_dormant"); if(GetDistance()>1) selectedPiece.removeClass("sgo_secret"); AnimateElementToElement(selectedPiece,cell); Message("You moved a ["+selectedPiece.text()+"]."); gameTimer = setTimeout(function(){ TurnFollowup(); },GetSpeed()); TrySelectBluePiece(null); /* selector */ SetSelectorMode("choose"); MoveElementToElement($("#sgo_selector"),cell); function GetDistance(){ let pieceCoords = GetCellCoords( GetCellAtPiece(selectedPiece) ); let destCoords = GetCellCoords( cell ); let dad = GetDirectionAndDistanceBetweenCoords(pieceCoords,destCoords); return dad.distance; } } function MovePathIsValid(dest){ let pieceCoords = GetCellCoords( GetCellAtPiece(selectedPiece) ); let destCoords = GetCellCoords(dest); let dad = GetDirectionAndDistanceBetweenCoords(pieceCoords,destCoords); if(dad.distance==0) return false; /* not a scout, must be 1 */ if(selectedPiece.text() != "2"){ return dad.distance==1; } /* scout moving 1 */ if(dad.distance==1) return true; /* scout moving far */ let tCell; for(let i=1;i<dad.distance;i++){ if(dad.direction=="up") tCell = GetCell(pieceCoords.x,pieceCoords.y-i); else if(dad.direction=="down") tCell = GetCell(pieceCoords.x,pieceCoords.y+i); else if(dad.direction=="left") tCell = GetCell(pieceCoords.x-i,pieceCoords.y); else if(dad.direction=="right") tCell = GetCell(pieceCoords.x+i,pieceCoords.y); if(tCell == null) return false; if(GetPieceAtCell(tCell) != null) return false; } return true; } function GetDirectionAndDistanceBetweenCoords(aaa,bbb){ let tDirection = ""; let tDistance; if(aaa.x == bbb.x){ tDistance = aaa.y - bbb.y; tDirection = (tDistance < 0) ? "down" : "up"; tDistance = Math.abs(tDistance); }else if(aaa.y == bbb.y){ tDistance = aaa.x - bbb.x; tDirection = (tDistance < 0) ? "right" : "left"; tDistance = Math.abs(tDistance); }else{ tDistance = 0; tDirection = "none"; }return {direction:tDirection,distance:tDistance}; } function GetBattleResult(attacker,defender){ let aaa = attacker.text(); let ddd = defender.text(); if(ddd==ICON_FLAG){ return "win flag"; }else if(ddd==ICON_BOMB){ if(aaa=="3") return "win bomb"; else return "lose bomb"; }else if(aaa=="1" && ddd=="10"){ return "win spied"; }else{ aaa = parseInt(aaa); ddd = parseInt(ddd); if(aaa == ddd) return "tie"; else if(aaa > ddd) return "win"; else return "lose"; } } function BattleFollowup(){ if(prevBattleResult=="win flag"){ gameState = "end"; $(".sgo_pieceRed").removeClass("sgo_secret"); }else{ if(prevBattleResult.includes("win")){ MoveElementToElement(selectedPiece,attackedPiece); attackedPiece.remove(); }else if(prevBattleResult.includes("lose")){ selectedPiece.remove(); }else if(prevBattleResult=="tie"){ selectedPiece.remove(); attackedPiece.remove(); } /* hide red again */ if(selectedPiece!=null && selectedPiece.hasClass("sgo_pieceRed")) selectedPiece.addClass("sgo_secret"); if(attackedPiece!=null && attackedPiece.hasClass("sgo_pieceRed")) attackedPiece.addClass("sgo_secret"); } } function TurnFollowup(){ if(prevBattleResult=="win flag") return; TrySelectBluePiece(null); SetSelectorMode(null); if(gameState=="your turn") gameState="enemy turn"; else gameState="your turn"; /*CheckForBattleGlitch();:::::::::::::::::::::::::::::: rare glitch */ if(gameState=="your turn"){ Message("Your turn. Move a piece."); gameTimer = null; }else if(gameState="enemy turn"){ Message("Enemy's turn. Please wait."); gameTimer = setTimeout(function(){ let entry = GetBestRedMove(); if(entry==null){ /* you win, enemy cannot move */ Message("Enemy cannot move. You win!"); gameState = "end"; $(".sgo_pieceRed").removeClass("sgo_secret"); }else if(entry.type=="move"){ RedMove(entry) }else if(entry.type=="attack"){ RedAttack(entry); } }, GetSpeed()==moveSpeedFast ? 100 : moveSpeedFast ); } } function RedMove(entry){ selectedPiece = entry.red; AnimateElementToElement(entry.red,entry.moveCell); Message("Enemy moved a piece."); gameTimer = setTimeout(function(){ TurnFollowup(); }, GetSpeed()==moveSpeedFast ? GetSpeed() : GetSpeed()*1.5 ); /* selector */ SetSelectorMode("choose"); MoveElementToElement($("#sgo_selector"),entry.moveCell); } function RedAttack(entry){ selectedPiece = entry.red; attackedPiece = entry.blue; prevBattleResult = GetBattleResult(selectedPiece,attackedPiece); /* ai data */ attackedPiece.removeClass("sgo_secret"); /* message and selector */ MoveElementToElement($("#sgo_selector"),attackedPiece); if(prevBattleResult=="win flag"){ Message("Enemy found your flag! You lose!"); SetSelectorMode("battleRed"); }else if(prevBattleResult.includes("win")){ Message("Enemy ["+selectedPiece.text()+"] defeated your ["+(attackedPiece.text()==ICON_BOMB?"bomb":attackedPiece.text())+"]."); SetSelectorMode("battleRed"); }else if(prevBattleResult.includes("lose")){ Message("Your ["+(attackedPiece.text()==ICON_BOMB?"bomb":attackedPiece.text())+"] defeated enemy ["+selectedPiece.text()+"]."); SetSelectorMode("battleBlue"); }else if(prevBattleResult=="tie"){ Message("Both ["+selectedPiece.text()+"] pieces were defeated."); SetSelectorMode("battleRed"); } /* animate towards */ let timeToStopEarly = GetSpeed()-(GetSpeed()/(GetDistance()+0.6)); AnimateElementToElement(selectedPiece,attackedPiece); selectedPiece.css("z-index",1); attackedPiece.css("z-index",0); selectedPiece.removeClass("sgo_secret");/* show self temporarily */ gameTimer = setTimeout(function(){ selectedPiece.stop(true,false); gameTimer = setTimeout(function(){/* delay */ BattleFollowup(); TurnFollowup(); },GetSpeed()) },timeToStopEarly);/* stop early */ function GetDistance(){ let pieceCoords = GetCellCoords( GetCellAtPiece(selectedPiece) ); let destCoords = GetCellCoords( GetCellAtPiece(attackedPiece) ); let dad = GetDirectionAndDistanceBetweenCoords(pieceCoords,destCoords); return dad.distance; } } function CheckForBattleGlitch(){ /* check if any two pieces share same cell */ /* if so, resolve the battle discreetly */ let reds = $(".sgo_pieceRed"); let blues = $(".sgo_pieceBlue"); let redCoords = []; let blueCoords = []; for(let i=0;i<reds.length;i++){ redCoords[i] = GetCellCoords(GetCellAtPiece(reds.eq(i))); }for(let i=0;i<blues.length;i++){ blueCoords[i] = GetCellCoords(GetCellAtPiece(blues.eq(i))); } for(let r=0;r<reds.length;r++){ let red = reds.eq(r); for(let b=0;b<blues.length;b++){ let blue = blues.eq(b); let rcoo = redCoords[r]; let bcoo = blueCoords[b]; if(rcoo.x != bcoo.x) continue; if(rcoo.y != bcoo.y) continue; /* if your turn, means cpu just moved */ let attacker = (gameState=="your turn") ? red : blue; let defender = (gameState=="your turn") ? blue : red; let battleResult = GetBattleResult(attacker,defender); if(battleResult.includes("win")){ defender.remove(); }else if(battleResult.includes("lose")){ attacker.remove(); }else if(battleResult=="tie"){ attacker.remove(); defender.remove(); } console.log("⚠️ Battle Glitch handled."); return; } } } /*------- enemy moving */ /* attack knowns with larger */ /* attack dormants with 2 or 3 */ /* attack non-dormants with largepieces */ function GetBestRedMove(){ let entries = GetRedBattleReport(); let moves = entries.filter(function(o){return o.type=="move";}); let attacks = entries.filter(function(o){return o.type=="attack";}); let sublist; /*================= any GOOD attacks? */ /* any known weaklings? */ sublist = attacks.filter(function(o){return o.blueIsSecret==false && CanWin(o);}); if(sublist.length>0) console.log("(A----) Attack a known weakling."); if(sublist.length>0) return RandomOne(sublist); /* any movers near our toughies? */ sublist = attacks.filter(function(o){return o.blueIsSecret;});/* knowns are dangerous */ sublist = sublist.filter(function(o){return o.blueIsDormant==false;}); sublist = sublist.filter(function(o){return o.red.text()=="8"||o.red.text()=="9"||o.red.text()=="10";}); if(sublist.length>0) console.log("(A----) Attack a mover with our 8-10."); if(sublist.length>0) return RandomOne(sublist); /* any dormant ones to ping with our 2 or 3? */ sublist = attacks.filter(function(o){return o.blueIsSecret;});/* knowns are dangerous */ sublist = sublist.filter(function(o){return o.blueIsDormant;}); sublist = sublist.filter(function(o){return o.red.text()=="2"||o.red.text()=="3";}); if(sublist.length>0) console.log("(A----) Attack a dormant with our 2-3."); if(sublist.length>0) return RandomOne(sublist); /*================ sometimes do a decent/risky attack */ if(Math.random()<0.5){ /* attack dormants with a 4 or 5 or 6 */ sublist = attacks.filter(function(o){return o.blueIsSecret;});/* knowns are dangerous */ sublist = sublist.filter(function(o){return o.blueIsDormant;}); sublist = sublist.filter(function(o){return o.red.text()=="4"||o.red.text()=="5"||o.red.text()=="6";}); if(sublist.length>0) console.log("(-B---) Attack a dormant with our 4-6."); if(sublist.length>0) return RandomOne(sublist); /* sometimes attack movers with a 4+ */ sublist = attacks.filter(function(o){return o.blueIsSecret;});/* knowns are dangerous */ sublist = sublist.filter(function(o){return o.blueIsDormant==false;}); sublist = sublist.filter(function(o){return o.red.text()!="1" && o.red.text()!="2" && o.red.text()!="3";}); if(sublist.length>0) console.log("(-B---) Attack a mover with our 4+."); if(sublist.length>0) return RandomOne(sublist); /* risky, attack unknowns with 2-7 */ sublist = attacks.filter(function(o){return o.blueIsSecret;});/* knowns are dangerous */ sublist = sublist.filter(function(o){return o.red.text()!="1" && o.red.text()!="8" && o.red.text()!="9" && o.red.text()!="10";}); if(sublist.length>0) console.log("(--C--) Risky attack with 2-7."); if(sublist.length>0) return RandomOne(sublist); /* risky, attack unknowns with 2-10 */ if(Math.random()<0.33){ sublist = attacks.filter(function(o){return o.blueIsSecret;});/* knowns are dangerous */ sublist = sublist.filter(function(o){return o.red.text()!="1";}); if(sublist.length>0) console.log("(---D-) Risky attack."); if(sublist.length>0) return RandomOne(sublist); } } /* sometimes move down */ if(Math.random()<0.5){ sublist = moves.filter(function(o){return o.moveDirection=="down";}); if(sublist.length>0) console.log("(-----) Move down."); if(sublist.length>0) return RandomOne(sublist); } /* move down or sideways */ sublist = moves.filter(function(o){return o.moveDirection!="up";}); if(sublist.length>0) console.log("(-----) Move down or sideways."); if(sublist.length>0) return RandomOne(sublist); /* random */ sublist = attacks.concat(moves); if(sublist.length>0) console.log("(----F) Random action."); if(sublist.length>0) return RandomOne(sublist); /* cannot move */ return null; function RandomOne(array){ return array[ Math.floor(Math.random()*array.length) ]; } function CanWin(entry){ /* win against KNOWN enemy? */ let battleResult = GetBattleResult(entry.red,entry.blue); return battleResult.includes("win"); } } let exampleEntry={ type:"move or attack", red:$(), moveCell:$(), moveDirection:"", moveDistance:0, blue:$(), blueIsDormant:false,/* blue piece has never moved */ blueIsSecret:false/* blue piece identity is unknown */ }; function GetRedBattleReport(){ let pieces = $(".sgo_pieceRed"); let entries = []; for(let i=0;i<pieces.length;i++){ let piece = pieces.eq(i); if(piece.text()==ICON_BOMB || piece.text()==ICON_FLAG) continue; /*::::::::::::::::::::::::::::::::::::::TODO support for scout, far movement */ DetermineEntry(piece,-1,0);/* left */ DetermineEntry(piece,1,0);/* right */ DetermineEntry(piece,0,-1);/* up */ DetermineEntry(piece,0,1);/* down */ } return entries; function DetermineEntry(piece,xxx,yyy){ let cell = GetCellAtPiece(piece); let coords = GetCellCoords(cell); let dest = LookForPieceOrCell(coords.x + xxx, coords.y + yyy); if(dest==null); else if(IsPiece(dest)){ if(dest.hasClass("sgo_pieceRed")) return;/* dont attack red pieces */ entries[entries.length] = { type: "attack", red: piece, blue: dest, blueIsDormant: dest.hasClass("sgo_dormant"), blueIsSecret: dest.hasClass("sgo_secret") }; }else{ let dad = GetDirectionAndDistanceBetweenCoords(coords,GetCellCoords(dest)); entries[entries.length] = { type: "move", red: piece, moveCell: dest, moveDirection: dad.direction, moveDistance: dad.distance/*::::::::::::::::: distance doesnt store correctly? */ }; } } function LookForPieceOrCell(x,y){ let tCell = GetCell(x,y); if(tCell==null) return null; let tPiece = GetPieceAtCell(tCell); if(tPiece==null) return tCell; return tPiece; } function IsPiece(element){ return element.hasClass("sgo_piece"); } function CanWin(attacker,defender){ /* win against KNOWN enemy? */ let battleResult = GetBattleResult(attacker,defender); return battleResult.includes("win"); } } /*============================================ */ function GetCell(x,y){ let cell = $("#sgo_cell"+x+"_"+y); return cell.length==0 ? null : cell; } function GetCellCoords(element){ let id = element.attr("id"); id = id.substr( "sgo_cell".length ); let xxx = parseInt( id.split("_")[0] ); let yyy = parseInt( id.split("_")[1] ); return {x:xxx,y:yyy}; } function MoveElementToElement(aaa,bbb){ let xxx = bbb.css("left"); let yyy = bbb.css("top"); aaa.css("left",xxx).css("top",yyy); } function AnimateElementToElement(aaa,bbb){ let xxx = bbb.css("left"); let yyy = bbb.css("top"); aaa.animate({left:xxx,top:yyy},GetSpeed()); } function GetPieceAtCell(cell){ let xxx = cell.css("left"); let yyy = cell.css("top"); let pieces = $(".sgo_piece"); for(let i=0;i<pieces.length;i++){ if(pieces.eq(i).css("left")==xxx && pieces.eq(i).css("top")==yyy) return pieces.eq(i); }return null; } function GetCellAtPiece(piece){ let xxx = piece.css("left"); let yyy = piece.css("top"); let cells = $(".sgo_cell"); for(let i=0;i<cells.length;i++){ if(cells.eq(i).css("left")==xxx && cells.eq(i).css("top")==yyy) return cells.eq(i); }return null; } function SetSelectorMode(mode){ let selector = $("#sgo_selector"); selector.removeClass("sgo_selectorChoose") .removeClass("sgo_selectorBattleRed") .removeClass("sgo_selectorBattleBlue"); if(mode==null) selector.hide(); else selector.show(); if(mode==null) ; else if(mode=="choose") selector.addClass("sgo_selectorChoose"); else if(mode=="battleRed") selector.addClass("sgo_selectorBattleRed"); else if(mode=="battleBlue") selector.addClass("sgo_selectorBattleBlue"); else console.log("mode '"+mode+"' not recognized"); } const messageAnim="sgo_messageFlash 0.5s linear 0s 1 normal"; function Message(m){ $("#sgo_message").html(m); /* reset animation */ let e=document.getElementById("sgo_message"); e.style.animation="none"; e.offsetHeight;/* trigger a "reflow" */ e.style.animation=messageAnim; } function ArrangeRedPiecesRandom(){ /* get top cells */ let cells = $(".sgo_cell"); let cell; for(let i=0;i<cells.length;i++){ cell = cells.eq(i); if(GetCellCoords(cell).y >= 4){ cells = cells.not(cell); i--; } } let pieces = $(".sgo_pieceRed"); let r,x,y; for(let i=0;i<pieces.length;i++){ r = Math.floor(Math.random()*cells.length); cell = cells.eq(r); MoveElementToElement(pieces.eq(i),cell); cells = cells.not(cell); } /* if flag on front row, swap it */ let tFlag = $(".sgo_pieceFlag.sgo_pieceRed").eq(0); if(GetCellCoords(GetCellAtPiece(tFlag)).y == 3){ while(true){/* allowed to retry */ let tPiece = pieces.eq(Math.floor(Math.random()*pieces.length)); let tCell = GetCellAtPiece(tPiece); if(GetCellCoords(tCell).y == 3) continue; MoveElementToElement(tPiece,tFlag); MoveElementToElement(tFlag,tCell); break; } } } function ArrangeBluePiecesRandom(){ /* get bottom cells */ let cells = $(".sgo_cell"); let cell; for(let i=0;i<cells.length;i++){ cell = cells.eq(i); if(GetCellCoords(cell).y < 6){ cells = cells.not(cell); i--; } } let pieces = $(".sgo_pieceBlue"); let r,x,y; for(let i=0;i<pieces.length;i++){ r = Math.floor(Math.random()*cells.length); cell = cells.eq(r); MoveElementToElement(pieces.eq(i),cell); cells = cells.not(cell); } } function ArrangeBluePiecesWithLayout(layout){ /* get bottom cells */ let cells = $(".sgo_cell"); let cell; for(let i=0;i<cells.length;i++){ cell = cells.eq(i); if(GetCellCoords(cell).y < 6){ cells = cells.not(cell); i--; } } /* do each digit of plan */ let pieces = $(".sgo_pieceBlue"); let x,y; for(let y=0;y<4;y++){ for(let x=0;x<10;x++){ let tPiece = GetPieceViaDigit(layout[y][x]); let tCell = GetCell(x,y+6); MoveElementToElement(tPiece,tCell); pieces = pieces.not(tPiece); } } function GetPieceViaDigit(s){ let clas = ".sgo_piece"; if(s=="B") clas+="Bomb"; else if(s=="F") clas+="Flag"; else if(s=="0") clas+="10"; else clas+=s; return pieces.filter(clas).eq(0); } } function ArrangeBluePieces_BombCenter(){ let layout = [ "8294B4B028", "323B4B4323", "5256BFB623", "5256717627" ]; ArrangeBluePiecesWithLayout(layout); } function ArrangeBluePieces_BombSides(){ let layout = [ "B4B0889B4B", "4B712337B4", "5566233665", "2222223F75" ]; ArrangeBluePiecesWithLayout(layout); } function ArrangeBluePieces_OpenLeft(){ let layout = [ "38107B66B7", "3283B5665B", "322222225B", "39744445BF" ]; ArrangeBluePiecesWithLayout(layout); }
</script> <style>
body{ background-color:rgb(78, 56, 87); }
</style> </head> <body> <center> <div id="stratego"></div> </center> </body> </html>
0000