언덕의 왕-Spacewar! 공백이나 문자를 넣지 마십시오.

Spacewar
를 해본 적이 있다면 ! 재미있는 게임이라는 것을 알고 있습니다. 당신이 모르는 경우, 이것은 가장 중요하고 가장 중요한 컴퓨터 게임 중 하나였습니다. 그리고 여전히 재미 있습니다! 내가 자란 복제본은 이것 입니다. 분명히 불행히도 Windows 만입니다. 그래서 나는 그것을 다시 만들었습니다!

KotH는 여기에서 호스팅됩니다 : PPCG-Spacewar! 언덕의 왕 . 게임이 어떻게 작동하는지 느끼기 위해 적어도 하나의 다른 봇과 대결하여 인간으로 플레이하는 것이 좋습니다.

게임

  • 한 프레임은 30 밀리 초 (즉, 초당 약 33 프레임)입니다.
  • 필드의 너비는 800 픽셀, 높이는 600 픽셀입니다.
  • 이 필드는 토 로이드 형으로, 필드 밖으로 이동하는 우주선과 미사일이 반대편에 다시 나타납니다.
  • 빨간색과 파란색의 두 우주선이 있습니다.
    • 빨간색은 x = 50에, 임의의 y는 50 (필드 높이-50) 픽셀 사이에 위치합니다.
    • 파란색은 x = (필드 너비-50) 및 임의의 y에서 50, (필드 높이-50) 픽셀 사이에 위치합니다.
    • 양면 x = (필드 너비) / 2.
  • 사용 가능한 컨트롤은 다음과 같습니다.
    • 프레임 당 시계 반대 방향으로 5도 왼쪽으로 돌립니다.
    • 오른쪽으로-프레임 당 5도 시계 방향으로 돌립니다.
    • 파이어 미사일-선박이 가리키는 방향으로 선박의 속도와 함께 프레임 당 추가 10 픽셀로 이동합니다.
    • 소방차-우주선이 가리키는 방향으로 프레임 당 0.30 픽셀의 우주선을 가속화합니다.
    • 초 공간 점프-25 %의 확률로 현장에서 임의의 좌표로 순간 이동합니다. 이 임의의 좌표는 태양 위에있을 수 있습니다.
  • 선박의 최대 속도는 엔진 출력에서 ​​프레임 당 15 픽셀, 중력 증폭시 프레임 당 40 픽셀입니다.
    • 프레임 당 15 픽셀보다 빠르게 이동할 경우 엔진 스러스트는 방향 만 변경하거나 속도를 늦출 수 있습니다.
  • 미사일에 관하여 :
    • 미사일은 직선으로 이동합니다.
    • 미사일은 0.1 초당 최대 1의 속도로 발사 될 수 있습니다.
    • 미사일의 수명은 2.25 초입니다.
    • 선박은 각각 최대 20 개의 미사일을 보유하고 있습니다.
    • 미사일은 내부적으로 점 입자입니다.
  • 중앙에 태양이있어 배에 매우 위험합니다. 가장 작은 접촉은 치명적입니다. 이 태양은 또한 미사일을 파괴합니다.
  • 태양에는 중력이 있습니다. 결과 가속은 5000 / (거리 ^ 2) 픽셀 / 프레임 ^ 2이며, 여기서 거리는 픽셀 단위입니다. 우주선과 미사일이 영향을받습니다.
  • 두 선박 모두 코, 왼쪽 날개 및 오른쪽 날개의 세 가지 타격 구역이 있습니다.
    • 코에 부딪 치면 즉시 사망합니다.
    • 어느 한 쪽 날개를 치면 우주선의 회전 속도와 엔진 가속이 절반으로 줄어 듭니다.
    • 두 날개가 모두 파괴되면 우주선을 조종 할 수 없으며 미사일 만 발사 할 수 있습니다.
  • 선박이 서로 충돌 할 수 있습니다.
    • 코 코의 충격은 두 선박 모두에게 치명적입니다.
    • 코 날개 충격은 날개를 파괴합니다.
    • 날개 날개 충격은 두 날개를 모두 파괴합니다.
  • 죽은 배는 1 초 후에 폭발 할 때까지 견고하고 동결됩니다.
  • 적어도 한 척의 선박이 사망 한 후 3 초 후에 필드가 재설정됩니다. 그때까지 태양과 남은 미사일은 여전히 ​​위험합니다.

원래 게임에는 치명적이고 파괴 할 수없는 소행성이 있지만 포함하지는 않습니다.

규칙

  • 봇은 JavaScript로 작성해야합니다.
  • 봇은 약 10 밀리 초로 결정을 제한해야합니다. 귀하의 봇으로 인해 일관된 지연이 발견되면 실격 처리하여 문제를 해결할 수 있도록 알려 드리겠습니다.
  • 봇은 다음에 액세스 할 수 있습니다.
    • 필드 너비 및 필드 높이
    • 태양의 위치와 반경
    • 두 선박의 위치, 회전, 속도, 모양, 미사일 재고 및 우주 공간 상태
    • 모든 미사일의 위치와 속도
  • 메시지가 표시되면 봇이 문자열 목록을 반환해야합니다.
    • 이러한 문자열은 다음 중 하나이어야한다 : turn left, turn right, fire engine, fire missile, hyperspace. 다른 문자열은 무시됩니다.
    • 중복이 있으면 첫 번째 만 기록됩니다.
    • hyperspace 다른 모든 것보다 우선합니다.
    • turn left그리고 turn right같은 시간에 아무런 영향을주지 않습니다.
    • fire engine 배에 코만 있거나 사망 한 경우에는 효과가 없습니다.
    • fire missile 미사일이 너무 최근에 발사 된 경우 아무런 효과가 없습니다.
  • 평소와 다른 방식으로 변경하면 봇은 다른 봇의 행동을 이용할 수 있습니다. 메타 게임을 장려하고 싶습니다.
    • 다른 봇을 모방 하지 않을 수 있습니다 . (즉, 마음을 읽지 않습니다.)
    • 게임 및 물리 코드에서 사용하는 변수를 설정할 수 없습니다 . (즉, 부정 행위 없음)

봇 구현 세부 사항

봇을 filename과 함께 자동으로 포함 된 자체 JavaScript 파일에 저장합니다 bot_<name>.js. 따라서 이것을 방해하거나 JavaScript의 함수 이름 지정을 방해하는 공백이나 문자를 넣지 마십시오. 즉, 다음과 같은 기능을 정의해야하기 때문이다 : <name>_setup(team)<name>_getActions(gameInfo, botVars). 페이지 아래에는 userbot에 대한 텍스트 영역이 있으며 코드를 테스트하기 위해 편집 할 수 있습니다.

<name>_setup(team)

이 함수는 유지하려는 변수를 정의하기위한 것입니다. team이 될 것입니다 "red"또는 "blue". 이 함수는 객체를 반환해야합니다. 다음과 같이 변수를 정의하십시오.

var vars = {};
vars['example'] = "example";
return vars;

vars객체는 다른 함수로 전달됩니다.

<name>_getActions(gameInfo, botVars)

botVars에서 반환 한 객체 <name>_setup(team)입니다. gameInfo다음 변수를 포함하는 객체입니다.

redScore
blueScore
timeLeft

fieldWidth
fieldHeight

sun_x
sun_y
sun_r //sun's radius

gravityStrength //acceleration in pixels/frame^2 at 1 pixel away from the sun's center
engineThrust    //acceleration in pixels/frame^2

speedLimit //maximum speed under engine power
maxSpeed   //maximum speed from gravity boosts

red_x
red_y
red_rot          //rotation in degrees
red_xv           //x velocity
red_yv           //y velocity
red_shape        //one of "full ship", "left wing", "right wing", "nose only"
red_missileStock //the number of missiles red has left
red_inHyperspace //true if red is in hyperspace
red_exploded     //until red explodes, it is still solid and hazardous
red_alive
// likewise for blue //

numMissiles
missiles //this is a list of objects, each with the following variables
  x
  y
  xv
  yv

봇은 이것들에 대한 모든 권한을 가지고 있습니다. 나는 당신 이 그것들에 쓸 수 있고 원래 변수에 영향을 미치지 않을 것이라고 확신 하지만 어쨌든하지 마십시오. 회전에 참고 사항 : 배송 포인트 는 배와 정렬 할 아래로,의 + y 방향으로, 그래서 아무것도 90 개도에 의해 상쇄 될 필요가있다. 또한 양의 회전은 시계 방향입니다.

이 함수는 봇의 동작을 나타내는 문자열 목록을 반환해야합니다. 예를 들면 다음과 같습니다 ["turn right","thrust"]. 이에 대한 자세한 내용은 규칙 섹션에 있습니다.

추가 세부 사항

다음을 사용할 수도 있습니다.

LineIntersection(L1, L2)

L1 및 L2는 2 요소 배열의 2 요소 배열입니다. 즉, L1 := [[x1,y1],[x2,y2]]하고 L2 := [[u1,v1],[u2,v2]]. 이 함수는 두 줄의 교집합을 계산하여 다음을 반환합니다 [[x,y], [a,b]]. [x,y]는 교점의 좌표이며 교점의 [a,b]각 선을 따라 얼마나 멀리 있는지를 나타내는 한 쌍의 비율입니다. 마찬가지로, a = 0.25교차점 길의 4 분에서 것을 의미 [x1,y1][x2,y2], 마찬가지로위한 b. 교차가 없으면 빈 배열이 반환됩니다.

window["shipShapes"]

var shipShapes = {
    'full ship': [[-8,16],[0,-8],[8,16]],
    'left wing': [[-8,16],[0,-8],[4,4],[0,8],[0,16]],
    'right wing':[[-4,4],[0,-8],[8,16],[0,16],[0,8]],
    'nose only': [[-4,4],[0,-8],[4,4],[0,8]]
};

이것들은 선박의 다각형 좌표입니다. 현재 좌표를 쉽게 얻기 위해 다음을 사용할 수도 있습니다.

getShipCoords(<color>)

getShipCoords("red")Red ‘s ship 정점의 현재 좌표와 마찬가지로 getShipCoords("blue")Blue 의 좌표를 반환합니다 . 이 좌표는 다음과 같은 목록에 [[x1,y1],[x2,y2],[x3,y3],...]있습니다. 다각형은 암시 적으로 닫혀 있으므로 첫 번째 좌표 쌍과 마지막 좌표 쌍 사이에 선이 있습니다.

게임 / 웹 사이트에서 사용중인 다른 변수 나 기능에 액세스하거나 변경할 수 없습니다. 그리고 함수 이름을 동일하게 지정하지 마십시오. 이것이 문제가 될 것이라고 예상하지는 않지만 봇이 게임 코드를 위반하면 그 가능성이 있습니다. 예외를 기록하거나 포착하지 않습니다.

승리

  • 봇의 모든 페어링은 두 가지 방식으로 최소 10 회 플레이해야합니다. (최소 20 게임 이상)
  • 전반적으로 가장 높은 승 / 패 비율을 가지도록 노력하십시오 . 만약 당신의 봇이 다른 봇에 대해서는 아주 잘 맞지만 다른 봇에 대해서는지는 경우, 두 사람에 대해 이기고 두 사람에 대해지는 것 (일반적으로)입니다.
  • 모든 봇에 대해 비율 (wins + 1) / (losses + 1)이 계산 된 다음 이러한 비율의 평균 및 표준 편차가 계산됩니다. 평균이 높을수록 우선 순위가 높고 평균이 서로 1 단위 이내 인 경우 분산이 낮을수록 우선 순위가 높습니다.
  • 채점은 오늘부터 일주일 이내에 또는 새로 제출하지 않은 3 일 후에 시작됩니다. 따라서 봇 쌍을 반복 할 필요가 없습니다.

무엇보다 재미있게 보내십시오!


리더 보드 (2016-01-08 05:15) :

#   Name                       Mean      StdDev
1.  Helios                     13.625    6.852
2.  EdgeCase                    8.335    8.155
3.  OpponentDodger              8.415    8.186
4.  OrbitBot                    5.110    6.294
5.  SunAvoider                  5.276    6.772
6.  DangitBobby                 3.320    4.423
7.  SprayAndPray                3.118    4.642
8.  Engineer                    3.903    6.315
9.  RighthandedSpasms           1.805    2.477
10. AttackAndComeBack           2.521    2.921
11. PanicAttack                 2.622    3.102
12. FullSpeedAhead              2.058    3.295
13. UhhIDKWhatToCallThisBot     2.555    3.406
14. MissilesPlusScore           0.159    0.228
15. Hyper                       0.236    0.332
16. RandUmmm                    0.988    1.329
17. Kamikaze                    0.781    1.793

참고 : 더 많은 게임을 실행하면 변경 될 수 있습니다. 또한 9-13 등급의 순서가 나를 귀찮게하므로 점수 방법을 조정하여 순위를 매기는 방법에 대한 직관에 더 잘 맞출 수 있습니다.

(평균과 표준 편차는 소수점 이하 세 자리로 반올림되었습니다. 또한 강조 표시를 망쳐 Hyper놓아야합니다 HYPER. : P)



답변

헬리오스

이 봇은 우주의 중심이거나 적어도 그가 생각한다고 생각합니다. 그가하는 첫 번째 일은 중대 오류를 수정하고 자신을 좌표계의 중심에 두는 것입니다. 그런 다음 그는 자신을 중심으로 무언가를 회전시킵니다.

그는 다른 (가짜) 태양을 좋아하지 않으므로 멀리 떨어지려고합니다. 그는 다른 봇을 좋아하지 않기 때문에 좋은 슈팅 위치에 있다면 쏴 버립니다.

function Helios_setup(team) {
    var botVars = {};
    botVars.myPrefix = team + "_";
    botVars.enemyPrefix = team == "red" ? "blue_" : "red_";
    return botVars;
}

function Helios_getActions(gameInfo, botVars) {
    var actions = [];
    var halfPi = Math.PI / 2;
    var engageAngle = Math.PI / 8;

    var field = {};
    field.width = gameInfo.fieldWidth;
    field.height = gameInfo.fieldHeight;
    field.halfWidth = field.width / 2;
    field.halfHeight = field.height / 2;
    field.posOffsetX = field.width * 3 / 2 - gameInfo[botVars.myPrefix + "x"];
    field.posOffsetY = field.height * 3 / 2 - gameInfo[botVars.myPrefix + "y"];
    field.posAngle = (450 - gameInfo[botVars.myPrefix + "rot"]) % 360 * Math.PI / 180;
    field.posSin = Math.sin(-field.posAngle);
    field.posCos = Math.cos(-field.posAngle);
    field.movOffsetXV = -gameInfo[botVars.myPrefix + "xv"];
    field.movOffsetYV = gameInfo[botVars.myPrefix + "yv"];
    field.movAngle = Math.atan2(-field.movOffsetYV, -field.movOffsetXV);
    field.movSin = Math.sin(-field.movAngle);
    field.movCos = Math.cos(-field.movAngle);

    function zeroIfUndefined(v) {
        return v === undefined ? 0 : v;
    }

    function sqr(x) {
        return x * x
    }

    function getEntity(source, prefix) {
        var tmpX = (field.posOffsetX + zeroIfUndefined(source[prefix + "x"])) % field.width - field.halfWidth;
        var tmpY = field.halfHeight - (field.posOffsetY + zeroIfUndefined(source[prefix + "y"])) % field.height;
        var tmpXV = zeroIfUndefined(source[prefix + "xv"]);
        var tmpYV = -zeroIfUndefined(source[prefix + "yv"]);
        var e = {};
        e.posX = tmpX * field.posCos - tmpY * field.posSin;
        e.posY = tmpX * field.posSin + tmpY * field.posCos;
        e.posR = Math.sqrt(sqr(e.posX) + sqr(e.posY));
        e.posPhi = Math.atan2(e.posY, e.posX);
        e.posXV = tmpXV * field.posCos - tmpYV * field.posSin;
        e.posYV = tmpXV * field.posSin + tmpYV * field.posCos;
        e.posV = Math.sqrt(sqr(e.posXV) + sqr(e.posYV));
        e.movX = tmpX * field.movCos - tmpY * field.movSin;
        e.movY = tmpX * field.movSin + tmpY * field.movCos;
        e.movR = Math.sqrt(sqr(e.movX) + sqr(e.movY));
        e.movPhi = Math.atan2(e.movY, e.movX);
        e.movXV = (tmpXV + field.movOffsetXV) * field.movCos - (tmpYV + field.movOffsetYV) * field.movSin;
        e.movYV = (tmpXV + field.movOffsetXV) * field.movSin + (tmpYV + field.movOffsetYV) * field.movCos;
        return e;
    }

    function getShip(prefix) {
        var ship = getEntity(gameInfo, prefix);
        ship.missileStock = gameInfo[prefix + "missileStock"];
        ship.inHyperspace = gameInfo[prefix + "inHyperspace"];
        ship.exploded = gameInfo[prefix + "exploded"];
        ship.alive = gameInfo[prefix + "alive"];
        return ship;
    }

    var myShip = getShip(botVars.myPrefix);
    myShip.movAngle = (field.posAngle - field.movAngle + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
    var enemyShip = getShip(botVars.enemyPrefix);
    var sun = getEntity(gameInfo, "sun_");

    enemyShip.intersectionLine = [[enemyShip.movX - enemyShip.movXV * 30, enemyShip.movY - enemyShip.movYV * 30],
            [enemyShip.movX + enemyShip.movXV * 30, enemyShip.movY + enemyShip.movYV * 30]];

    var intersection = LineIntersection([[0, 0], [Math.cos(myShip.movAngle) * 10 * 30, Math.sin(myShip.movAngle) * 10 * 30]],
            enemyShip.intersectionLine);
    if (intersection.length == 2) {
        myShip.intersection = Math.abs(intersection[1][0] / 2 + 0.5 - intersection[1][1]);
    }
    intersection = LineIntersection([[0, 0], [Math.cos(myShip.movAngle - 0.001) * 10 * 30, Math.sin(myShip.movAngle - 0.001) * 10 * 30]],
            enemyShip.intersectionLine);
    if (intersection.length == 2) {
        myShip.intersectionLeft = Math.abs(intersection[1][0] / 2 + 0.5 - intersection[1][1]);
    }
    intersection = LineIntersection([[0, 0], [Math.cos(myShip.movAngle + 0.001) * 10 * 30, Math.sin(myShip.movAngle + 0.001) * 10 * 30]],
            enemyShip.intersectionLine);
    if (intersection.length == 2) {
        myShip.intersectionRight = Math.abs(intersection[1][0] / 2 + 0.5 - intersection[1][1]);
    }

    function danger() {
        var tmp1 = sqr(sun.movXV) + sqr(sun.movYV);
        var tmp2 = tmp1 == 0 ? 0 : Math.max(0, Math.min(1, ((-sun.movX) * sun.movXV + (-sun.movY) * sun.movYV) / tmp1));
        var dis = Math.sqrt(sqr(sun.movX + tmp2 * sun.movXV) + sqr(sun.movY + tmp2 * sun.movYV));
        if (dis < 30) {
            return true;
        }
        var shipLine1 = [[-16, 8], [-16, -8]];
        var shipLine2 = [[-16, 8], [8, 0]];
        var shipLine3 = [[-16, -8], [8, 0]];
        if (gameInfo.missiles !== undefined) {
            for (var i = 0; i < gameInfo.missiles.length; i++) {
                var missile = getEntity(gameInfo.missiles[i], "");
                var missileLine = [[missile.movX + missile.movXV * 0.5, missile.movY + missile.movYV * 0.5],
                        [missile.movX + missile.movXV * 3, missile.movY + missile.movYV * 3]];
                if (LineIntersection(shipLine1, missileLine).length == 2 ||
                        LineIntersection(shipLine2, missileLine).length == 2 ||
                        LineIntersection(shipLine3, missileLine).length == 2) {
                  return true;
                }
            }
        }
        return false;
    }

    function fire() {
        return enemyShip.alive && !enemyShip.inHyperspace && myShip.intersection !== undefined &&
            myShip.intersection < 0.1 + myShip.missileStock / 200;
    }

    function evadeSun() {
        if ((sun.movPhi >= 0 && myShip.movAngle < 0) || (sun.movPhi <= 0 && myShip.movAngle > 0)) {
            actions.push("fire engine");
        }
        if (sun.movPhi > 0) {
            if (Math.abs(myShip.movAngle) < halfPi) {
                actions.push("turn right");
            } else {
                actions.push("turn left");
            }
        } else {
            if (Math.abs(myShip.movAngle) < halfPi) {
                actions.push("turn left");
            } else {
                actions.push("turn right");
            }
        }
    }

    function aim() {
        if (myShip.intersection !== undefined && myShip.intersectionLeft !== undefined && myShip.intersectionLeft < myShip.intersection) {
            actions.push("turn left");
        } else if (myShip.intersection !== undefined && myShip.intersectionRight !== undefined && myShip.intersectionRight < myShip.intersection) {
            actions.push("turn right");
        } else {
            if (enemyShip.posPhi > 0) {
                actions.push("turn left");
            } else {
                actions.push("turn right");
            }
        }
        if (myShip.posV < 2 || (enemyShip.alive && (enemyShip.movXV >= 0 || myShip.missileStock == 0))) {
            actions.push("fire engine");
        }
    }

    function brake() {
        if (myShip.movAngle > 0) {
            actions.push("turn left");
        } else {
            actions.push("turn right");
        }
        if (Math.abs(myShip.movAngle) > Math.PI * 3 / 4) {
            actions.push("fire engine");
        }
    }

    function engage() {
        if (enemyShip.missileStock > 0) {
            if ((enemyShip.posPhi > 0 && enemyShip.posPhi < engageAngle) || enemyShip.posPhi < -engageAngle) {
                actions.push("turn right");
            } else {
                actions.push("turn left");
            }
        } else {
            if (enemyShip.posPhi > 0) {
                actions.push("turn left");
            } else {
                actions.push("turn right");
            }
        }
        actions.push("fire engine");
    }

    if (myShip.alive && !myShip.inHyperspace) {
        if (danger()) {
            actions.push("hyperspace");
        }
        if (fire()) {
            actions.push("fire missile");
        }
        if (enemyShip.exploded || enemyShip.inHyperspace || sun.movR < 150 || (sun.movR < 300 && Math.abs(sun.movPhi) < Math.PI)) {
            evadeSun();
        } else if (enemyShip.posR < 300 || myShip.intersection !== undefined) {
            aim();
        } else if (myShip.posV > 10) {
            brake();
        } else {
            engage();
        }
    }

    return actions;
}

답변

SunAvoider

이것은 단지 태양으로부터 멀리 떨어지려고합니다. 그것은 아주 잘합니다 … 하나 또는 두 개의 날개가 모두 파괴 될 때까지, 그것은 일반적으로 떨어지기 전에 단지 시간 문제입니다.

function SunAvoider_setup(team) {
    var botVars = {};

    botVars["color"] = team;

    return botVars;
}

function SunAvoider_getActions(gameInfo, botVars) {
    var actions = [];

    if (gameInfo[botVars["color"]+"_alive"]) {
        var shipx = gameInfo[botVars["color"]+"_x"];
        var shipy = gameInfo[botVars["color"]+"_y"];
        var sunx = gameInfo["sun_x"];
        var suny = gameInfo["sun_y"];
        var dx = shipx - sunx;
        var dy = shipy - suny;
        var dis = Math.sqrt(dx*dx+dy*dy);
        var fireEngineChance = (dis-100)/(gameInfo["fieldHeight"]/2);

        if (Math.random() > fireEngineChance){ actions.push("fire engine") }

        var ang1 = gameInfo[botVars["color"]+"_rot"]+90;
        var ang2 = Math.degrees( Math.atan2(dy, dx) );
        var angDiff = ang2 - ang1;
        if (angDiff < -180) { //http://stackoverflow.com/a/7869457/1473772
            angDiff += 360;
        } else if (angDiff > 180) {
            angDiff -= 360;
        }

        if (angDiff >= 0) {
            actions.push("turn left");
        } else if (angDiff < 0) {
            actions.push("turn right");
        }
    }

    return actions;
}

답변

엣지 케이스

태양에서 멀리 떨어진지도 가장자리까지 전속력으로 비행하십시오! 자신이 태양을 향하고있는 것을 발견하면 가장자리로 돌아 가기 위해 몸을 돌리면서 촬영을 시작합니다. 또한 태양에 닿을 때 초 공간으로 들어갑니다.

function EdgeCase_setup(team) {
  var botVars = {};
  botVars["color"] = team;
  return botVars;
}

function EdgeCase_getActions(gameInfo, botVars) {
  var actions = [];

  // Get our ship's position
  var rotation, x, y, opponentAlive;
  if(botVars.color == "red") {
    rotation = gameInfo.red_rot;
    x = gameInfo.red_x;
    y = gameInfo.red_y;
    opponentAlive = gameInfo.blue_alive;
  }
  else if(botVars.color == "blue") {
    rotation = gameInfo.blue_rot;
    x = gameInfo.blue_x;
    y = gameInfo.blue_y;
    opponentAlive = gameInfo.red_alive;
  }

  // Calculate our rotation compared to the sun in degrees
  var sunX = gameInfo.sun_x,
      sunY = gameInfo.sun_y,
      angle = Math.atan2(sunY - y, sunX - x) * 180 / Math.PI,
      rotationToSun = (rotation - angle + 360) % 360;

  // Check if we need to hyperspace to avoid the sun
  var rX = x - sunX,
      rY = y - sunY,
      distanceFromSun = Math.sqrt(rX * rX + rY * rY) - gameInfo.sun_r;
  if(distanceFromSun < 30) actions.push("hyperspace");
  else {

    // Turn away from the sun
    if(rotationToSun > 90 && rotationToSun < 270) {
      actions.push("turn right");
    }
    else actions.push("turn left");

    // Fire engines if we're pointing away from the sun
    if(rotationToSun > 180) {
      actions.push("fire engine");
    }

    // If we shoot while our opponent's dead we can only kill ourself
    else if(opponentAlive) actions.push("fire missile");
  }

  return actions;
}

답변

궤도 봇

현재 타겟팅 또는 충돌 방지 기능 이 없습니다 . 태양을 공전 시키려고한다.

편집 : 이제 임팩트가 임박하면 초 공간으로 이동합니다.

function OrbitBot_setup(team) {
  var botVars = {};

  botVars.color = team;
  return botVars;
}


function OrbitBot_getActions(gameInfo, botVars) {
  var actions = [];

  function getVar(name) {
    return gameInfo[botVars.color + "_" + name];
  }

  function getEnemyVar(name) {
    var eColor;
    if (botVars.color == 'blue') {
        eColor = 'red';
    } else {
        eColor = 'blue';
    }
    return gameInfo[eColor + "_" + name];
  }

  function distance(x1, y1, x2, y2) {
    return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
  }

  function toroidDistance(x1, y1, x2, y2) {
    dx = Math.abs(x1 - x2);
        while (dx > gameInfo.fieldWidth) {
        dx -= gameInfo.fieldWidth;
    }
    dx = Math.min(dx, gameInfo.fieldWidth - dx);
    dy = Math.abs(y1 - y2);
        while (dx > gameInfo.fieldHeight) {
        dx -= gameInfo.fieldHeight;
    }
    dy = Math.min(dy, gameInfo.fieldHeight - dy);
    return Math.sqrt(dx*dx+dy*dy);
  }

  function angleDistance(theta1, theta2) {
    var d = theta1 - theta2;
    while (d < 0 || d > Math.PI) {
      if (d < 0) {
        d += Math.PI * 2;
      }
      if (d > Math.PI * 2) {
        d -= Math.PI * 2;
      } else if (d > Math.PI) {
        d = Math.PI * 2 - d;
      }
    }
    return d;
  }

  function toRad(degrees) {
    return degrees / 180 * Math.PI;
  }

  function cap(x, y, limit) {
    var r = x*x+y*y;
    if (r < limit * limit) {
        r = Math.sqrt(r);
        x = x * r / limit;
      y = y * r / limit;
    }
    return [x,y];
  }

  var shape = getVar('shape');

  if (shape != 'nose only') {
    var broken = shape != 'full ship';
    var sunX = gameInfo.sun_x,
      sunY = gameInfo.sun_y,
      sunG = gameInfo.gravityStrength;

    function desirability(x, y, vx, vy) {     //Borrowed from a useless bot.
      var lowest = distance(x, y, sunX, sunY) - 5;
      var missiles = gameInfo.missiles;
      for (var i = 0; i < missiles.length; i++) {
        var mx = missiles[i].x + missiles[i].xv / 2;
        var my = missiles[i].y + missiles[i].yv / 2;
        lowest = Math.min(lowest, toroidDistance(x, y, mx, my) - distance(0, 0, missiles[i].xv, missiles[i].yv));
      }
      return lowest - 16;
    }

    var x = getVar("x"),
      y = getVar("y"),
      vx = getVar("xv"),
      vy = getVar("yv");

    function desirabilityByAcceleration(ax, ay) {//Borrowed from a useless bot.
        var x1 = x,
            y1 = y,
          vx1 = vx,
          vy1 = vy;
      var speed = distance(0,0,vx1,vy1);
      var limit = Math.max(gameInfo.speedLimit, speed);

      vx1 += ax;
      vy1 += ay;
      var temp = cap(vx1, vy1, limit);
      vx1 = temp[0];
      vy1 = temp[1];


      var dx = x1 - sunX;
      var dy = y1 - sunY;
      var dis = Math.sqrt(dx*dx+dy*dy);
      if (dis > 5){
        var force = sunG / (dis * dis);
      } else {
        var force = sunG /5;
      }
      vx1 -= force*dx/dis;
      vy1 -= force*dy/dis;

      var temp = cap(vx1, vy1, 40);
      vx1 = temp[0];
      vy1 = temp[1];

      x1 += vx1;
      y1 += vy1;

      return desirability(x1, y1, vx1, vy1);
    }

    var r = distance(sunX, sunY, x, y);
    var theta = Math.atan((y - sunY) / (x - sunX));

    var sunA = sunG/r/r,
            sunAx = -Math.cos(theta) * sunA,
        sunAy = -Math.sin(theta) * sunA;

    var dv = Math.sqrt(sunG / r);
    var dvx = -dv * Math.sin(theta);
    var dvy = dv * Math.cos(theta);
    if (distance(-dvx, -dvy, vx, vy) < distance(dvx, dvy, vx, vy)) {
      dvx = -dvx;
      dvy = -dvy;
    }

    var dax = dvx - vx;
    var day = dvy - vy;

    var dAngle = Math.atan(day / dax);
    if (dax < 0) {
        dAngle += Math.PI;
    }
    var cAngle = toRad(getVar('rot') - 90);
    var dLeft = angleDistance(cAngle - toRad(broken ? 2.5 : 5), dAngle);
    var dRight = angleDistance(cAngle + toRad(broken ? 2.5 : 5), dAngle);
    var dNeither = angleDistance(cAngle, dAngle);
    if (dLeft < dRight && dLeft < dNeither) {
      actions.push('turn left');
    } else if (dRight < dLeft && dRight < dNeither) {
      actions.push('turn right');
    }

    var cax = Math.cos(cAngle) * (broken ? .15 : .3);
    var cay = Math.sin(cAngle) * (broken ? .15 : .3);

    var ax = 0;
    var ay = 0;

    if (distance(cax, cay, dax, day) < distance(0, 0, dax, day)) {
      actions.push('fire engine');
      ax = cax;
      ay = cay;
    }

    if (desirabilityByAcceleration(ax, ay) <= 16) {
        actions.push('hyperspace');
    }

  }

  return actions;
}

답변

오른 손잡이

그 이름은 매우 묘사 적입니다. 선택 turn right0.5 확률 fire engine0.5 확률 및 fire missile0.8 확률. 놀라 울 정도로 어려웠습니다. 주로 예측할 수 없기 때문입니다.

function RighthandedSpasms_setup(team) {
    var botVars = {};

    botVars["color"] = team;

    return botVars;
}

function RighthandedSpasms_getActions(gameInfo, botVars) {
    var actions = [];

    if (gameInfo[botVars["color"]+"_alive"]) {
        if (Math.random() > 0.5) { actions.push("turn right") }
        if (Math.random() > 0.5) { actions.push("fire engine") }
        if (Math.random() > 0.8) { actions.push("fire missile") }
    }

    return actions;
}

답변

랜드

이 도전에는 무작위 봇이 필요했습니다. 골프에 대한 보너스 포인트?

function RandUmmm_setup(t){
    function P(n,t,r,o,e,f,g){for(o=[e=1<<(f=n.length)];e;)for(t=e.toString(2),r=g=t.length,o[--e]=[];r;)~-t[--r]||o[e].push(n[r+f-g]);return o}var q=P(["fire missile","turn right","fire engine","turn left"]);q.pop();
    return {color:t,m:function(){return q[Math.random()*q.length|0]}};
}

function RandUmmm_getActions(g,b){
    return b.m();
}

답변

기사

위험 할 때 초 공간을 사용하는 것을 좋아합니다. 그것이 진정한 힘인지 확인하려면 브라우저의 콘솔을 열고을 입력하십시오 overideHyperspace = 0;. 세미콜론을 잊어 버리면 크리스마스에 ASI를 받게됩니다.

function Engineer_setup(t){
    return{c:t,C:"red0blue".split(0)[+(t=="red")]};
}

function Engineer_getActions(gameInfo,botVars){
    var actions = [];

    function d(x1,y1,x2,y2){return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))}
    function hS(g){return d(g.sun_x,g.sun_y,g[botVars.c+"_x"],g[botVars.c+"_y"])<50}
    function enemyDist(g){return d(g[botVars.c+"_x"],g[botVars.c+"_y"],g[botVars.C+"_x"],g[botVars.C+"_y"]);}

    function hSm(g){
        // get closest missile
        var r = (g.missiles||[{x:10000,y:10000}]).reduce(function(p,c){return Math.min(d(c.x,c.y,g[botVars.c+"_x"],g[botVars.c+"_y"]),p)},Infinity);
        return r<18;
    }
    function dF(g){
        var a = Math.degrees(Math.atan2(g[botVars.C+"_y"]-g[botVars.c+"_y"],g[botVars.C+"_x"]-g[botVars.c+"_x"]));
        var tP = (g[botVars.c+"_rot"]+360-a)%360;
        return [a,tP];
    }
    function lOr(g){
        var tP = dF(g)[1];
        return 90<tP&&tP<270?"turn left":"turn right";
    }
    function thrust(g){
        return Math.abs(dF(g)-g[botVars.c+"_rot"]);
    }

    // are we too close to the sun or a missile?
    if(hS(gameInfo)||hSm(gameInfo))actions.push("hyperspace");

    // should we fire?
    if(enemyDist(gameInfo)<200)actions.push("fire missile");

    // direction function
    actions.push(lOr(gameInfo,botVars));

    if(Math.random()<.7)actions.push("fire engine");
    return actions;
}