Viktory Script

Komputer - an AI for Viktory II

ของเมื่อวันที่ 02-05-2024 ดู เวอร์ชันล่าสุด

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey to install this script.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Viktory Script
// @namespace    http://tampermonkey.net/
// @version      2024-05-03
// @description  Komputer - an AI for Viktory II
// @author       Wilbo Baggins / Stephen Montague
// @match        http://gamesbyemail.com/Games/Viktory2
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gamesbyemail.com
// ==/UserScript==

(function() {
    // Begin main script.
    console.log("Hello Viktory.");

    // Wait for the page to load.
    waitForKeyElements("#Foundation_Elemental_7_savePlayerNotes", () => {

        // Alias the game.
        let thisGame = Foundation.$registry[7];

        // Add button to page.
        console.log("Adding AI agent control button.");
        addButton("Run Komputer", runKomputer, thisGame);

        window.moveIntervalId = null;
        window.movingUnitIndex = 0;
        window.moveWave = 0;
        window.battleIsSelected = false;
        window.IS_KOMPUTER_READY = true;
   }); // End wait for page load

})(); // End main script.


function runKomputer(thisGame)
{
    console.log("Checking movePhase.");
    switch(thisGame.movePhase)
    {
        case 0:
            console.log("Game reset.");
            setTimeout(function(){runKomputer(thisGame)}, 600);
            break;
        case 2:
            console.log("Placing capital.");
            placeCapital(thisGame);
            break;
        case 5:
            console.log("Movement Wave: " + window.moveWave);
            moveUnits(thisGame);
            break;
        case 6:
            console.log("Retreat is not an option. Returning to battle.");
            thisGame.movePhase = 5;
            thisGame.update();
            break;
        case 11:
            console.log("Placing reserves.");
            placeReserves(thisGame);
            break;
        default:
            console.log("Unhandled movePhase: " + thisGame.movePhase);
    }
}


function placeCapital(thisGame)
{
    // Explore 5 hexes, handle water
    let hexOrder = thisGame.getMapCustomizationData();
    const hasWater = (hexOrder.indexOf("w") > -1) ? true : false;
    if (hasWater)
    {
        while (waterCount(hexOrder) > 2)
        {
            thisGame.swapWaterForLand();
            hexOrder = thisGame.getMapCustomizationData();
        }
        thisGame.playOptions.mapCustomizationData = [hexOrder[0], hexOrder[4], hexOrder[1], hexOrder[3], hexOrder[2]].join("");
    }
    thisGame.customizeMapDoAll(true);

    // Place capital & explore adjacent
    let pieceIndex = (thisGame.player.team.color === 0) ? 9 : 51;
    let destinationScreenPoint = thisGame.pieces[pieceIndex].$screenRect.getCenter();
    thisGame.placeCapitalMouseDown(destinationScreenPoint);
    thisGame.overlayCommitOnClick();
    setTimeout(function(){
        thisGame.customizeMapDoAll(true);
        if (thisGame.player.team.color === 0)
        {
            thisGame.endMyTurn();
            window.IS_KOMPUTER_READY = true;
            console.log("Done.");
        }
        // Place first town according to Peter M's strategy. Later reserves are scattered randomly.
        else
        {
            if (thisGame.movePhase === 11)
            {
                thisGame.reserveOnMouseDown(thisGame, function(){return true},0);
                pieceIndex = (Math.random() > 0.5) ? 36 : 53; // Choose left or right side randomly
                destinationScreenPoint = thisGame.pieces[pieceIndex].$screenRect.getCenter();
                thisGame.placeReserveOnMouseUp(destinationScreenPoint)
                thisGame.overlayCommitOnClick();
                setTimeout(function(){
                    thisGame.customizeMapDoAll(true);
                    thisGame.reserveOnMouseDown(thisGame, function() {return true},0);
                    thisGame.placeReserveOnMouseUp( destinationScreenPoint );
                    thisGame.endMyTurn();
                    window.IS_KOMPUTER_READY = true;
                    console.log("Done.");
                }, 1000);
            }
        }}, 800);
}

function waterCount(stringHexData)
{
      let count = 0;
      for (let i = 0; i < stringHexData.length; i++)
      {
          if (stringHexData.charAt(i)==="w")
          {
              count++;
          }
      }
      return count;
}

function getRandomItem(items)
{
    const MAX = items.length;
    const RANDOM_INDEX = getRandomIndexExclusive(MAX);
    return items[RANDOM_INDEX];
}

function getRandomIndexExclusive(max)
{
    return Math.floor(Math.random() * max);
}

async function moveUnits(thisGame)
{
    const moveIntervalPeriod = 800;
    const moveDelay = 200;
    setTimeout(async function(){
        switch (window.moveWave)
        {
            case 0: {
                console.log("May move land units.");
                const landUnits = getMovableLandUnits(thisGame, thisGame.perspectiveColor);
                moveEachUnitByInterval(thisGame, landUnits, moveIntervalPeriod);
                break;
            }
            case 1: {
                console.log("May move frigates.");
                const frigates = getFrigates(thisGame, thisGame.perspectiveColor);
                moveEachUnitByInterval(thisGame, frigates, moveIntervalPeriod);
                break;
            }
            case 2: {
                console.log("May move all available.");
                const landUnits = getMovableLandUnits(thisGame, thisGame.perspectiveColor);
                const frigates = getFrigates(thisGame, thisGame.perspectiveColor);
                const allMilitaryUnits = landUnits.concat(frigates);
                moveEachUnitByInterval(thisGame, allMilitaryUnits, moveIntervalPeriod);
                break;
            }
            case 3:{
                console.log("Handling all battles.");
                const battlePiece = findNextBattle(thisGame);
                if (battlePiece)
                {
                    fightBattle(thisGame, battlePiece);
                }
                else
                {
                    window.moveWave++;
                    runKomputer(thisGame);
                }
                break;
            }
            case 4: {
                console.log("Ending movement.");
                // Reset movement, restart to place reserves or stop for next player.
                window.moveWave = 0;
                let lastPlayerColor = thisGame.perspectiveColor;
                endMovementPhase(thisGame);
                window.moveIntervalId = await setInterval(function(){
                    if (thisGame.movePhase === 11)
                    {
                        clearInterval(window.moveIntervalId);
                        runKomputer(thisGame);
                    }
                    else if(lastPlayerColor !== thisGame.perspectiveColor)
                    {
                        clearInterval(window.moveIntervalId);
                    }
                }, 200);
            }
        }
    }, moveDelay);
}


function getMovableLandUnits(thisGame, color)
{
    let landUnits = [];
    for (const piece of thisGame.pieces)
    {
        // Skip reserve units
        if (piece.valueIndex === - 1)
        {
            continue;
        }
        for (const unit of piece.units)
        {
            if (unit.color === color && unit.type !== "f" && unit.canMove())
            {
                landUnits.push(unit);
            }
        }
    }
    return landUnits;
}

function getFrigates(thisGame, color)
{
    let frigates = [];
    for (const piece of thisGame.pieces)
    {
        // Skip reserve units
        if (piece.valueIndex === - 1)
        {
            continue;
        }
        for (const unit of piece.units)
        {
            if (unit.color === color && unit.type === "f")
            {
                frigates.push(unit);
            }
        }
    }
    return frigates;
}


function endMovementPhase(thisGame)
{
    thisGame.endMyMovement();
}

async function moveEachUnitByInterval(thisGame, movableUnits, intervalPeriod)
{
    window.moveIntervalId = await setInterval(function(){
        // Get the unit to move.
        let unit = movableUnits[window.movingUnitIndex];
        let stoppedToExplore = false;
        // On the initial wave, which has only land units, give a 50% chance to move, allowing land units to wait for an incoming frigate.
        const shouldMoveThisWave = (window.moveWave > 0) ? true : ((Math.random() > 0.5) ? true : false);
        if (unit && shouldMoveThisWave)
        {
            let mayBombard = true;
            if (!unit.movementComplete || unit.hasUnloadables())
            {
                const possibleMoves = unit.getMovables();
                if (possibleMoves)
                {
                    // Get destination and track any pending battle.
                    const pieceIndex = getRandomItem(possibleMoves).index;
                    if (thisGame.pieces[pieceIndex].hasRollingOpponent(thisGame.player.team.color))
                    {
                        mayBombard = false;
                    }
                    // Move unit.
                    let originScreenPoint = unit.screenPoint;
                    moveUnitSimulateMouseDown(thisGame, originScreenPoint);
                    let destinationScreenPoint = thisGame.pieces[pieceIndex].$screenRect.getCenter();
                    moveUnitSimulateMouseUp(thisGame, destinationScreenPoint);
                    // Commit to explore after some processing time.
                    let normalPopup = document.getElementById("Foundation_Elemental_7_overlayCommit");
                    if (normalPopup || document.getElementById("Foundation_Elemental_7_customizeMapDoAll"))
                    {
                        if (normalPopup)
                        {
                            thisGame.overlayCommitOnClick();
                        }
                        setTimeout(function(){thisGame.customizeMapDoAll(true)}, 100);
                        stoppedToExplore = true;
                    }
                } // End if possibleMoves
            } // End if not movementComplete or hasUnloadables
            // Bombard on the final wave.
            const isTimeToBombard = (window.moveWave === 2) ? true : false;
            if (isTimeToBombard)
            {
                if (mayBombard && unit.canBombard() && unit.piece !== null)
                {
                    const bombardablePoints = unit.getBombardables();
                    if (bombardablePoints)
                    {
                        bombard(thisGame, unit, bombardablePoints);
                    }
                }
            }
        }// End if unit check
        // Decide how to continue
        if (stoppedToExplore && !unit.movementComplete)
        {
            // Pass: move same unit next interval.
        }
        else if ( (window.movingUnitIndex + 1) < movableUnits.length )
        {
            // Move the next unit next interval.
            window.movingUnitIndex++;
        }
        else
        {
            // Clear wave interval, reset moving unit index, cue next.
            clearInterval(window.moveIntervalId);
            window.movingUnitIndex = 0;
            window.moveWave++;
            runKomputer(thisGame);
        }
    }, intervalPeriod);
}


function bombard(thisGame, unit, bombardablePoints)
{
    thisGame.bombardUnitsMouseDown(unit.screenPoint);
    const randomTargetPoint = getRandomItem(bombardablePoints);
    const targetScreenPoint = thisGame.pieces.findAtPoint(randomTargetPoint).$screenRect.getCenter();
    const firingDelay = 100;
    setTimeout(function(){
        thisGame.bombardUnitsMouseUp(targetScreenPoint);
        const commitDelay = 100;
        setTimeout(function(){
            thisGame.overlayCommitOnClick();
            // Apply hits.
            const applyHitsDelay = 200;
            setTimeout(function(){
                if (thisGame.battleData)
                {
                    const data = thisGame.getBattleData();
                    if (data && data.piece.defenderBattleInfo &&
                        data.piece.defenderBattleInfo.decisionNeeded)
                    {
                        applyHits(thisGame, data.piece.index, data);
                    }
                }
                const reviewDelay = 200;
                setTimeout(function(){
                    thisGame.pieces[unit.piece.index].bombardOkClick(thisGame.player.team.color);
                }, reviewDelay)
            }, applyHitsDelay);
        }, commitDelay);
    }, firingDelay);
}

function findNextBattle(thisGame)
{
    for (let piece of thisGame.pieces)
    {
        if (piece.hasBattle(thisGame.player.team.color, thisGame.player.team.rulerColor))
        {
            return piece;
        }
    }
    return null;
}

function fightBattle(thisGame, battlePiece)
{
    // Select battle
    if (!window.battleIsSelected) // Todo: think about case where human selects a random battle, then tries to runKomputer.
    {
        thisGame.moveUnitsMouseDown(battlePiece.$screenRect.getCenter());
        window.battleIsSelected = true;
    }
    // Do prebattle artillery
    if (document.getElementById("Foundation_Elemental_7_battleOk"))
    {
        battlePiece.preBattleOkClick(thisGame.player.team.color);
    }
    // Roll loop
    const rollDelay = 200;
    setTimeout(function roll(){
        thisGame.overlayCommitOnClick();
        // Apply hits.
        const applyHitsDelay = 200;
        setTimeout(function(){
            if (thisGame.battleData)
            {
                const data = thisGame.getBattleData();
                if (data && data.piece.attackerBattleInfo &&
                    data.piece.attackerBattleInfo.decisionNeeded ||
                    data.piece.defenderBattleInfo &&
                    data.piece.defenderBattleInfo.decisionNeeded)
                {
                    applyHits(thisGame, battlePiece.index, data);
                }
            }
            // Close battle after review.
            const battleReviewDelay = 1600;
            setTimeout(function(){
                thisGame.pieces[battlePiece.index].battleOkClick(thisGame.player.team.color);
                const reRollDelay = 600;
                setTimeout(function(){
                    if (document.getElementById("Foundation_Elemental_7_overlayCommit"))
                    {
                        roll();
                    }
                    else
                    {
                        window.battleIsSelected = false;
                        runKomputer(thisGame);
                    }
                }, reRollDelay);
            }, battleReviewDelay);
        }, applyHitsDelay);
    }, rollDelay);

}

function applyHits(thisGame, pieceIndex, battleData)
{
    const thisPiece = thisGame.pieces[pieceIndex];
    const attackerColor = thisGame.player.team.color;
    const defenderColor = (attackerColor === 0) ? 1 : 0;
    const attackerUnitList = thisPiece.getMilitaryUnitList(attackerColor);
    const defenderUnitList = thisPiece.getDefenderUnitList(defenderColor);
    const attackerHitThreshold = thisGame.getHitThreshold(attackerColor);
    const defenderHitThreshold = thisGame.getHitThreshold(defenderColor);
    thisPiece.defenderBattleInfo = thisPiece.getBattleInfo(defenderUnitList, battleData.attackerRolls, attackerHitThreshold,true);
    thisPiece.attackerBattleInfo = thisPiece.getBattleInfo(attackerUnitList, battleData.defenderRolls, defenderHitThreshold,false);
    // Choose highest value hits on the defender.
    if (thisPiece.defenderBattleInfo.decisionNeeded)
    {
        const hitCount = (thisPiece.defenderBattleInfo.numHit - thisPiece.defenderBattleInfo.numTacticalHit);
        thisPiece.hitHighestMilitaryUnits(defenderUnitList, hitCount, false);
    }
    // Choose lowest value hits on the attacker.
    if (thisPiece.attackerBattleInfo.decisionNeeded)
    {
        const hitCount = (thisPiece.attackerBattleInfo.numHit - thisPiece.attackerBattleInfo.numTacticalHit);
        thisPiece.hitLowestMilitaryUnits(attackerUnitList, hitCount, false);
    }
    setTimeout(function(){ thisPiece.battleOkClick(attackerColor) }, 200);
}


async function placeReserves(thisGame)
{
    // Just in case, clear and reset previous phases.
    clearInterval(window.moveIntervalId);
    window.battleIsSelected = false;
    window.movingUnitIndex = 0;
    window.moveWave = 0;
    window.reserveIntervalId = await setInterval(placeReserveUnit, 1000, thisGame);
}

function placeReserveUnit(thisGame){
    const reserveUnits = thisGame.player.team.reserveUnits;
    const controlsCapital = thisGame.doesColorControlTheirCapital(thisGame.player.team.color);
    let hasPlayableReserveUnit = false;
    if (thisGame.movePhase === 11 && reserveUnits.length > 0)
    {
        for (let i = 0; i < reserveUnits.length; i++)
        {
            if (thisGame.couldPlaceReserveUnit(reserveUnits[i], thisGame.player.team.color, controlsCapital))
            {
                thisGame.reserveOnMouseDown(thisGame, function(){return true}, i);
                hasPlayableReserveUnit = true;
                break;
            }
        }
        // Place reserve unit on a valid destination
        if (hasPlayableReserveUnit)
        {
            const movingUnitType = thisGame.pieces.getNewPiece().movingUnit.type;
            const destinationBoardPoint = (movingUnitType === "t" || movingUnitType === "y") ? (
                getRandomItem(thisGame.getBuildables(thisGame.player.team.color, thisGame.player.team.rulerColor)) ) : (
                getRandomItem(thisGame.pieces.getReservables(thisGame.player.team.color,thisGame.player.team.rulerColor,movingUnitType, controlsCapital)) );
            const destinationScreenPoint = thisGame.screenRectFromBoardPoint(destinationBoardPoint).getCenter();
            thisGame.placeReserveOnMouseUp(destinationScreenPoint);
            if (document.getElementById("Foundation_Elemental_7_overlayCommit"))
            {
                thisGame.overlayCommitOnClick();
            }
            setTimeout(function(){thisGame.customizeMapDoAll(true);}, 800);
        }
    }
    // End of reserves - ensure ready for next player.
    else
    {
        clearInterval(window.reserveIntervalId);
        window.IS_KOMPUTER_READY = true;
        console.log("Done.");
    }
}


function addButton(text, onclick, pointerToGame) {
    let style = {position: 'absolute', top: '776px', left:'24px', 'z-index': '9999', "-webkit-transition-duration": "0.6s", "transition-duration": "0.6s", overflow: 'hidden'}
    let button = document.createElement('button'), btnStyle = button.style
    //let parent = document.getElementById("Foundation_Elemental_7_bottomTeamTitles"); // Todo: add something like this for better control of position relative to parent.
    //parent.appendChild(button);  // This hides the button under other elements, regardless of z-index. Maybe, instead, add the element via GBE framework.
    document.body.appendChild(button) // For now, this works well enough.
    button.setAttribute("class", "button_runKomputer");
    button.innerHTML = text
    button.onclick = function() {onclick(pointerToGame)};
    Object.keys(style).forEach(key => btnStyle[key] = style[key])

    // Add Button Press Transition 1
    const cssButtonClassString1 = `.button_runKomputer:after{content: ""; background: #90EE90; display: block; position: absolute; padding-top: 300%; padding-left: 350%; margin-left: -20px!important; margin-top: -120%; opacity: 0; transition: all 1.0s}`;
    const styleTag1 = document.createElement("style");
    styleTag1.innerHTML = cssButtonClassString1;
    document.head.insertAdjacentElement('beforeend', styleTag1);

    // Add Button Press Transition 2
    const cssButtonClassString2 = `.button_runKomputer:active:after{padding: 0; margin: 0; opacity: 1; transition: 0s}`;
    const styleTag2 = document.createElement("style");
    styleTag2.innerHTML = cssButtonClassString2;
    document.head.insertAdjacentElement('beforeend', styleTag2);
}


function moveUnitSimulateMouseDown(thisGame, screenPoint)
{
    thisGame.maybeResetReservesByMouseUp();
    thisGame.moveBombardUnitsMouseOut(screenPoint=thisGame.constrainPoint(screenPoint));
    thisGame.maybeHideOverlay();
    let unit=thisGame.militaryUnitFromScreenPoint(screenPoint,null,thisGame.player.team.color,thisGame.player.team.rulerColor,true);
    if (unit)
    {
        thisGame.setTargetPoints(unit.getMovables());
        thisGame.onLeftMouseUp="moveUnitsMouseUp";
        thisGame.onMouseMove=null;
        thisGame.onMouseOver=null;
        thisGame.onMouseOut=null;
        let piece=thisGame.pieces.getNewPiece();
        piece.setMovingUnit(unit);
        thisGame.forcingFrigateUnload=(unit.isFrigate() && thisGame.targetPointsAllLand);
        if (thisGame.forcingFrigateUnload)
            unit.centerUnload(screenPoint.subtract(0,5),piece);
        else
        {
            unit.setVisibility(false);
            piece.center(screenPoint.subtract(0,5));
        }
    }
    else
    {
        let boardPoint;
        let piece;
        if ((boardPoint=thisGame.boardPointFromScreenPoint(screenPoint)) &&
            (piece=thisGame.pieces.findAtPoint(boardPoint)) &&
            piece.hasBattle(thisGame.player.team.color,thisGame.player.team.rulerColor))
        {
            piece.setBorder(true);
            thisGame.battleIndex=piece.index;
            thisGame.pushMove("Battle",thisGame.logEntry(6,piece.index,piece.boardValue,piece.getOpponentColor(thisGame.player.team.color)),piece,piece.hasPreBattle(thisGame.player.team.color) ? "processPreBattleMove" : "processStartBattleMove",true,"beginBattle","cancel");
            thisGame.update();
        }
        else
            thisGame.showOverlay();
    }
}

function moveUnitSimulateMouseUp(thisGame, screenPoint)
{
    thisGame.onMouseMove=null;
    thisGame.onLeftMouseUp=null;
    if (thisGame.lastLoadableUnit)
    {
        thisGame.lastLoadableUnit.setHilite(false);
        thisGame.lastLoadableUnit=null;
    }
    let movingPiece=thisGame.pieces.getNewPiece();
    let boardPoint=thisGame.boardPointFromScreenPoint(screenPoint);
    if (thisGame.isTargetPoint(boardPoint))
    {
        let targetPiece=thisGame.pieces.findAtPoint(boardPoint);
        if (movingPiece.movingUnit.isLandUnit() &&
            targetPiece.isWater())
        {
        let oldPiece=movingPiece.movingUnit.piece;
        let loadableUnit=thisGame.militaryUnitFromScreenPoint(screenPoint,null,movingPiece.movingUnit.color,movingPiece.movingUnit.rulerColor,false,false,true);
        let log=thisGame.logEntry(7,oldPiece.index,oldPiece.boardValue,targetPiece.index,targetPiece.boardValue,movingPiece.movingUnit.type,loadableUnit.type);
        loadableUnit.loadCargo(movingPiece.movingUnit);
        movingPiece.setMovingUnit(null);
        oldPiece.updateUnitDisplay();
        loadableUnit.piece.updateUnitDisplay();
        thisGame.setTargetPoints(null);
        let nltd=thisGame.nothingLeftToDo();
        thisGame.pushMove("Load",log,targetPiece,"processMoveUnitMove",nltd,nltd ? "commitEndOfTurn" : null);
        }
        else
        if (movingPiece.movingUnit.isFrigate() &&
            targetPiece.isLand())
        {
            let oldPiece=movingPiece.movingUnit.piece;
            let log=thisGame.logEntry(8,oldPiece.index,oldPiece.boardValue,targetPiece.index,targetPiece.boardValue,movingPiece.movingUnit.getActiveCargoType(),movingPiece.movingUnit.type);
            movingPiece.movingUnit.unloadCargo(targetPiece);
            movingPiece.movingUnit.piece.updateUnitDisplay();
            movingPiece.setMovingUnit(null);
            targetPiece.updateUnitDisplay();
            thisGame.setTargetPoints(null);
            let nltd=thisGame.nothingLeftToDo();
            thisGame.pushMove("Unload",log,targetPiece,"processMoveUnitMove",nltd,nltd ? "commitEndOfTurn" : null);
        }
        else
        {
            let oldPiece=movingPiece.movingUnit.piece;
            let tp=thisGame.getTargetPoint(boardPoint);
            let log=thisGame.logEntry(9,oldPiece.index,oldPiece.boardValue,targetPiece.index,targetPiece.boardValue,movingPiece.movingUnit.type,tp.spacesNeeded);
            movingPiece.movingUnit.moveTo(targetPiece,tp.spacesNeeded,tp.retreatIndex);
            movingPiece.setMovingUnit(null);
            oldPiece.updateUnitDisplay();
            targetPiece.updateUnitDisplay();
            thisGame.setTargetPoints(null);
            let nltd=thisGame.nothingLeftToDo();
            thisGame.pushMove("Move",log,targetPiece,"processMoveUnitMove",nltd,nltd ? "commitEndOfTurn" : null);
        }
    }
    else
    {
        thisGame.setTargetPoints(null);
        thisGame.onLeftMouseUp=null;
        thisGame.onMouseMove="moveBombardUnitsMouseMove";
        thisGame.onMouseOver="moveBombardUnitsMouseMove";
        thisGame.onMouseOut="moveBombardUnitsMouseOut";
        if (movingPiece.movingUnit)
        {
            movingPiece.movingUnit.setVisibility(true);
            let piece=movingPiece.movingUnit.piece;
            movingPiece.setMovingUnit(null);
            if (piece.boardPoint.equals(boardPoint) &&
                piece.hasBattle(thisGame.player.team.color,thisGame.player.team.rulerColor))
            {
                piece.setBorder(true);
                thisGame.battleIndex=piece.index;
                thisGame.pushMove("Battle",thisGame.logEntry(6,piece.index,piece.boardValue,piece.getOpponentColor(thisGame.player.team.color)),piece,piece.hasPreBattle(thisGame.player.team.color) ? "processPreBattleMove" : "processStartBattleMove",true,"beginBattle","cancel");
            }
        }
    }
    thisGame.update();
}


/**
     * Greasemonkey Wrench by CoeJoder, for public use.
     * Source: https://github.com/CoeJoder/GM_wrench/blob/master/src/GM_wrench.js
	 * Detect and handle AJAXed content.  Can force each element to be processed one or more times.
	 *
	 * @example
	 * GM_wrench.waitForKeyElements('div.comments', (element) => {
	 *   element.innerHTML = 'This text inserted by waitForKeyElements().';
	 * });
	 *
	 * GM_wrench.waitForKeyElements(() => {
	 *   const iframe = document.querySelector('iframe');
	 *   if (iframe) {
	 *     const iframeDoc = iframe.contentDocument || iframe.contentwindow.document;
	 *     return iframeDoc.querySelectorAll('div.comments');
	 *   }
	 *   return null;
	 * }, callbackFunc);
	 *
	 * @param {(string|function)} selectorOrFunction The selector string or function.
	 * @param {function}          callback           The callback function; takes a single DOM element as parameter.  If
	 *                                               returns true, element will be processed again on subsequent iterations.
	 * @param {boolean}           [waitOnce=true]    Whether to stop after the first elements are found.
	 * @param {number}            [interval=300]     The time (ms) to wait between iterations.
	 * @param {number}            [maxIntervals=-1]  The max number of intervals to run (negative number for unlimited).
*/
function waitForKeyElements (selectorOrFunction, callback, waitOnce, interval, maxIntervals) {
    if (typeof waitOnce === "undefined") {
        waitOnce = true;
    }
    if (typeof interval === "undefined") {
        interval = 300;
    }
    if (typeof maxIntervals === "undefined") {
        maxIntervals = -1;
    }
    var targetNodes =
        typeof selectorOrFunction === "function"
    ? selectorOrFunction()
    : document.querySelectorAll(selectorOrFunction);

    var targetsFound = targetNodes && targetNodes.length > 0;
    if (targetsFound) {
        targetNodes.forEach(function (targetNode) {
            var attrAlreadyFound = "data-userscript-alreadyFound";
            var alreadyFound = targetNode.getAttribute(attrAlreadyFound) || false;
            if (!alreadyFound) {
                var cancelFound = callback(targetNode);
                if (cancelFound) {
                    targetsFound = false;
                } else {
                    targetNode.setAttribute(attrAlreadyFound, true);
                }
            }
        });
    }

    if (maxIntervals !== 0 && !(targetsFound && waitOnce)) {
        maxIntervals -= 1;
        setTimeout(function () {
            waitForKeyElements(selectorOrFunction, callback, waitOnce, interval, maxIntervals);
        }, interval);
    }
};