블로그를 다시 시작하는 과정에서 기존에 있던 모든 포스트들을 초기화하기로 결심했고 모든 것이 초기화되었다.1 초기화 후에 로보코드를 통한 블로그 유입이 있어 로보코드에 관한 정보를 찾는 분들이 있다는 것을 알 수 있었다.

다행히 기존 글을 쓸 당시의 한글문서와 이미지가 남아 있기에 당시 글을 복원하여 올리게 되었으며, 그 당시 이 글의 방향이 처음 로보코드를 접하는 사람에게 로보코드에 대한 소개와 사이트들에 대한 간단한 소개를 전하고자 함에 있었다. 따라서 거의 원형을 가져온 이 글 또한 전반적인 소개에 대해 작성이 되어있지만, 로보코드의 로봇 제작에 대한 세세한 부분은 작성되 있지 않다.

로보코드의 세세한 작성에 대한 내용은 Robowiki 및 Robocode Repository에서 챔피언들을 통해서 알아볼 것을 추천한다.

1. 로보코드란?

로보코드는 IBM 개발자 한 분이 만든 로봇 전쟁 시뮬레이터이다. 로봇 개발을 통한 JAVA 교육 툴이다.

2. 로보코드 설치 방법

로보코드의 설치 방법은 로보코드 사이트에서 무료로 다운로드 할 수 있다. 사이트 주소는 아래와 같다.

http://robocode.sourceforge.net/

이 사이트는 로보코드 개발에 많은 도움이 되는 사이트이니 로보코드 개발을 하고자하면 즐겨 찾기에 등록해두는 것이 좋다.

3. 로보코드 개발에 도움이 되는 사이트 (한글)

1) http://loanbro.tistory.com/4

로보코드의 기본적인 설명이나 이클립스의 플러그인을 통한 개발 방법이 잘 나와 있다. 로보코드 안의 자체 에디터만으로는 개발에 어려움이 있으니 이클립스를 통해 개발하는 것을 추천한다.

2) http://kjs1981.tistory.com/category/로보코드

기본적인 설정이나 개발 방법에 대해 알았다면 로봇의 움직임, 로보코드에서 사용되는 메소드나 클래스들 등을 이용하고자 할 때 도움이 되는 사이트이다.

3) http://cafe.daum.net/tgvcstudy

이 카페의 자유강좌 부분에도 위 블로그와 유사한 설명들이 있다. 로보코드의 움직임이나 향상된 로봇을 만들 때 꼭 필요하다.

4. 로보코드 개발에 도움이 되는 사이트 (영문)

1) http://robocode.sourceforge.net/

위에서 설명했던 로보코드 사이트이다. 사실 영어에 익숙한 사람이라면 한글 사이트보다는 영문 사이트를 찾는 것이 좋다. 한글 사이트에는 많은 정보가 없어서 결국에는 영문 사이트 곳곳을 기웃거리는 자신을 발견하게 된다.

이 사이트에서 꼭 봐야하는 부분은 다음과 같다.

  • Robocode API (JAVA) : API 문서 사이트
  • RoboWiki : 로보코드에 대한 개발 기술
  • Robocode Repository : 로봇들의 공유 사이트

2) Rock ‘em, sock ‘em Robocode! / Rock ‘em, sock ‘em Robocode: Round2

이 사이트는 로보코드에 대한 전반적인 설명이 들어있다. 영문에 익숙하다면 한글 사이트 여럿 참조하는 것보다 이 문서 하나 읽는게 도움이 될 것이다. 하지만, 필자도 제대로 읽어보지 않았기에 내용에 대한 확신은 없다.

3) Secrets from the Robocode masters

로보코드 마스터들의 조언들이다. 샘플이 아닌 다른 사람들과의 대전에 꼭 필요한 기술들이 기술되어있다. 이 사이트와 RoboWiki 사이트를 통해 향상된 기술들을 배울 수 있다.

5. 향상된 로봇

향상된 로봇의 개발은 위에서 언급했듯이 RoboWiki 사이트와 마스터들의 조언 사이트들을 통해 배울 수 있다.

그리고 Robowiki 및 Robocode Repository에서 챔피언들을 통해서 이 코드들이 어떻게 사용되었는지 확인하며 개발한다면 큰 도움이 될 수 있다.

6. 기타

위 사이트들에서 찾을 수 있는 로보코드 챔피언들의 배포 파일을 볼 때 .class 파일로 되어있거나 혹 .java 파일이지만 가독성이 떨어진다면 클래스(Class)파일을 자바(Java)파일로 변환시켜주는 자바 디컴파일러인 “jd-gui” 라는 파일을 통해 본다면 편하게 읽을 수 있다.

Downloads

7. 글을 마치며

학교 프로젝트를 하기 위해 시작한 로보코드였다. 밤을 새워가며 여러 사이트를 돌아다니고, 몇 시간씩 이클립스를 붙잡고 오류와 씨름하며 지냈다. 프로젝트를 마치면서 지금에서야 편하게 글을 쓸 수 있다.

JAVA를 공부하면서 단순한 예제가 아닌 조그만 규모의 프로젝트를 하며 공부하고 싶을 때, 재미없는 예제가 아니라 눈에 보이는 재미있는 예제를 공부하고 싶을 때라면 이 로보코드를 공부하면 좋을 것 같다.

이 프로젝트를 하면서 수없이 보게 된 Fermet라는 챔피언이 있다. 이 챔피언의 소스코드 안에는 상속, 입출력, 멀티쓰레딩 등 자바를 배우면서 어려운 부분들이 사용되고 있다. 이를 분석 하고 필자 또한 이를 사용해보려 노력하면서 조금 더 이 부분을 공부할 수 있게 되었다.

이 글을 마치면서 다시 돌아보게 된 로보코드는, 로보코드를 개발한 사람의 목적인 ‘ JAVA 교육 툴 ’에 딱 들어맞았다.


다시 글을 복원하면서 아주 조금은 손을 보았지만 그래도 아쉬운 부분이 조금 있다. 아니 많이 있다. 하지만 그것을 전부 충족시키기에는 로보코드를 한지 오래되어서 요단강을 넘어 기억 저편에 있는 아이들이 필요하기에 할 수 없었다. 조금 아쉬움이 남는 부분은 아래의 코드로 대체하고자 한다.

Sample팀과의 전투 혹은 많은 인원과의 전투가 주로 이루어지는 경우에는 적어도 팀 로봇 중 하나를 기본 로봇 중에 있는 “Walls”이라는 로봇을 바탕으로 만들면 생존을 높일 수 있다. “Walls”라는 로봇은 벽을 타고 돌아다니기 때문에 많은 규모의 싸움시에 적이 공멸하기까지 시간을 버티는데 도움이 된다.

아래는 Sample 로봇 “Walls”의 코드이다.

/***********************************************************************
     * Copyright (c) 2001-2013 Mathew A. Nelson and Robocode contributors
     * All rights reserved. This program and the accompanying materials
     * are made available under the terms of the Eclipse Public License v1.0
     * which accompanies this distribution, and is available at
     * http://robocode.sourceforge.net/license/epl-v10.html
   ***********************************************************************/
    package sample;


    import robocode.HitRobotEvent;
    import robocode.Robot;
    import robocode.ScannedRobotEvent;

    import java.awt.*;


    /**
   * Walls - a sample robot by Mathew Nelson, and maintained by Flemming N. Larsen
   * <p/>
   * Moves around the outer edge with the gun facing in.
   *
   * @author Mathew A. Nelson (original)
   * @author Flemming N. Larsen (contributor)
   */
    public class Walls extends Robot {

        boolean peek; // Don't turn if there's a robot there
        double moveAmount; // How much to move

        /**
       * run: Move around the walls
       */
        public void run() {
            // Set colors
            setBodyColor(Color.black);
            setGunColor(Color.black);
            setRadarColor(Color.orange);
            setBulletColor(Color.cyan);
            setScanColor(Color.cyan);

            // Initialize moveAmount to the maximum possible for this battlefield.
            moveAmount = Math.max(getBattleFieldWidth(), getBattleFieldHeight());
            // Initialize peek to false
            peek = false;

            // turnLeft to face a wall.
            // getHeading() % 90 means the remainder of
            // getHeading() divided by 90.
            turnLeft(getHeading() % 90);
            ahead(moveAmount);
            // Turn the gun to turn right 90 degrees.
            peek = true;
            turnGunRight(90);
            turnRight(90);

            while (true) {
                // Look before we turn when ahead() completes.
                peek = true;
                // Move up the wall
                ahead(moveAmount);
                // Don't look now
                peek = false;
                // Turn to the next wall
                turnRight(90);
            }
        }

        /**
       * onHitRobot:  Move away a bit.
       */
        public void onHitRobot(HitRobotEvent e) {
            // If he's in front of us, set back up a bit.
            if (e.getBearing() > -90 && e.getBearing() < 90) {
                back(100);
            } // else he's in back of us, so set ahead a bit.
            else {
                ahead(100);
            }
        }

        /**
       * onScannedRobot:  Fire!
       */
        public void onScannedRobot(ScannedRobotEvent e) {
            fire(2);
            // Note that scan is called automatically when the robot is moving.
            // By calling it manually here, we make sure we generate another scan event if there's a robot on the next
            // wall, so that we do not start moving up it until it's gone.
            if (peek) {
                scan();
            }
        }
    }

아래는 Sample 로봇 “Walls”을 바탕으로 만든 코드이다.

    package avangers;

    import java.awt.geom.Point2D;
    import robocode.*;
    import robocode.util.Utils;
    import java.awt.Color;
    import java.awt.geom.*;
    import java.util.*;
    import java.security.cert.*;



    public class Hulk extends Avanger {

        /* 적 변수 선언 및 저장 */
        Hashtable targets;            // 적의 데이터를 해시 테이블에 저장
        Enemy target;
        Enemy trackTarget=null;

        /*변수 선언 */
        final double PI = Math.PI;    

        int hitBullet=0;
        int hitWall=0;
        int hitMyBullet=0;
        int dodgeCount=0;
        double bulletBearing;
        int teamCount=3;
        boolean live=true;
        boolean eLive=true;

        double gapX;
        double gapY;
        double gap;

        double move;

        /* 역할 파트*/
        boolean leader;
        boolean dodgeMan;

        /* 레이더 부분 */
        Radar rad;

        /* 전략부분 */
        TournamentStrategy tStrategy;

        /* 회피 참조 변수 */
        Dodge dodD;
        WallSmooth dodW;
        DodgeBullet dodB;

        public void run() {

            /* 적 변수 초기화 */
            targets = new Hashtable();
            target = new Enemy(this, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0L, 0.0D,"Loki", 0.0D, 0.0D);

            /* 로봇 설정 부분 */
            setAdjustGunForRobotTurn(true);
            setAdjustRadarForGunTurn(true);

            setColors(Color.GREEN, Color.ORANGE, Color.BLACK);

            /*레이더 부분*/
            turnRadarRightRadians(2*PI);
            rad=new Radar(this,targets);

            /*리더 체크 부분 */
            leader=isLeader();

            /* 전략부분 */
            tStrategy = new TournamentStrategy(this,targets);

            dodD= new Dodge(this,targets);
            dodW=new WallSmooth(this,targets);
            dodB=new DodgeBullet(this,targets);


            /* 벽으로 이동*/            
            if(eLive){
                move = Math.max(getBattleFieldWidth()-18-getX(), getBattleFieldHeight()-18-getY());
                turnLeft(getHeading() % 90);
                ahead(move);
                turnRight(90);
            }

            while(true) {

                rad.meleeRadar(this);            

                /* 벽에 출돌을 최소화 및 경로 이탈시 재 진입 */
                if(eLive){
                    if(getX()<=30&&getY()<=30){
                        ahead(600-getY()-18);
                        turnRight(90);
                    }
                    else if(getX()<=30&&getY()>=570){
                        ahead(800-getX()-18);
                        turnRight(90);
                    }
                    else if(getX()>=770&&getY()>=570){
                        ahead(getY()-18);
                        turnRight(90);
                    }
                    else if(getX()>=770&&getY()<=30){
                        ahead(getX()-18);
                        turnRight(90);
                    }
                    else if(getY()<=30){
                        ahead(getX()-18);
                        turnRight(90);
                    }
                    else if(getX()<=30){
                        ahead(600-getY()-18);
                        turnRight(90);
                    }
                    else if(getY()>=570){
                        ahead(800-getX()-18);
                        turnRight(90);
                    }
                    else if(getX()>=770){
                        ahead(getY()-18);
                        turnRight(90);
                    }
                    else{
                        move = Math.max(getBattleFieldWidth()-18-getX(), getBattleFieldHeight()-18-getY());
                        turnLeft(getHeading() % 90);
                        ahead(move);
                        turnRight(90);    

                    }
                }
                //tStrategy.tStrategyTo12();

                /*적 리더가 사망시 드로이드 제거를 위해 벽에서 이동 탈출*/
                if(!eLive)
                    dodW.wallSmoothMove();

                execute();
            }
        }


        /* 스캔 Event */
        public void onScannedRobot(ScannedRobotEvent e) {

            /* 적 Data 생성 및 업데이트 */
            Enemy en;

            if(!isTeammate(e.getName()))
                if (targets.containsKey(e.getName())) {
                    en = (Enemy)targets.get(e.getName());
                    en.update(e.getEnergy(), getX() + e.getDistance() * Math.cos(Math.toRadians(normalRelativeAngle(90.0D - (getHeading() + e.getBearing())))), getY() + e.getDistance() * Math.sin(Math.toRadians(normalRelativeAngle(90.0D - (getHeading() + e.getBearing())))), e.getHeading(), e.getBearing(), e.getVelocity(), getTime(), e.getDistance(), e.getHeadingRadians(), e.getBearingRadians());
                }
                else {
                    en = new Enemy (this, e.getEnergy(), getX() + e.getDistance() * Math.cos(Math.toRadians(normalRelativeAngle(90.0D - (getHeading() + e.getBearing())))), getY() + e.getDistance() * Math.sin(Math.toRadians(normalRelativeAngle(90.0D - (getHeading() + e.getBearing())))), e.getHeading(), e.getBearing(), e.getVelocity(), getTime(), e.getDistance(), e.getName(), e.getHeadingRadians(), e.getBearingRadians());
                    targets.put(e.getName(),en);
                }

            gapX = e.getDistance() * Math.cos(Math.toRadians(normalRelativeAngle(90.0D - (getHeading() + e.getBearing()))));
            gapY = e.getDistance() * Math.sin(Math.toRadians(normalRelativeAngle(90.0D - (getHeading() + e.getBearing()))));
            gap = Math.toDegrees(Math.atan2(gapX, gapY));

            turnGunRight(Utils.normalRelativeAngleDegrees(gap - getGunHeading()));
            if(!isTeammate(e.getName()))
                /* 거리 200 이하 */
                if(e.getDistance()<=200){
                    setBulletColor(Color.RED);
                    fire(3);
                }
                /* 거리 500 이하 */
                else if(e.getDistance()<=500){
                    setBulletColor(Color.YELLOW);
                    fire(3);
                }

                /* 거리 500 이상 */
                else{
                    setBulletColor(Color.BLACK);
                    fire(3);
                }

        }

        /* 적 로봇의 생사 유무 Event */
        public void onRobotDeath(RobotDeathEvent e) {
            if(isTeammate(e.getName())) // 팀원인 경우 팀원 숫자를 한명 줄임
                teamCount--;
            else{
                Enemy en = (Enemy)targets.get(e.getName());
                en.live = false;
                if(en.eLeader)
                    eLive=false;
            }

        }

        /* 적과 충돌 했을 때 */
        public void onHitRobot(HitRobotEvent e) {
            if (e.getBearing() > -90 && e.getBearing() < 90) {
                back(100);
            }
            else {
                ahead(100);
            }
        }


        /* 적에 의해 피탄 당했을 때 Event. 방향 변환에 사용*/
        public void onHitByBullet(HitByBulletEvent e) {
            Enemy en = (Enemy)targets.get(e.getName());

            hitBullet++;
            dodgeCount++;
            if(dodgeCount>10)
                dodgeCount=0;        
            bulletBearing=e.getBearing();

        }

        /* 적을 피격했을 때 Event */
        public void onBulletHit(BulletHitEvent e){
            hitMyBullet++;
        }

        /* 벽에 출돌했을 때 Event */
        public void onHitWall(HitWallEvent e) {

            hitWall++;
        }

        /* 특정 좌표로 이동 */
        void goTo(double x, double y){
            double angle = normalAbsoluteHeading(Math.toDegrees(1.570796326794897D - Math.atan2(getY() - y, getX() - x)));
            int dir = 1;
            double distance=0;

            angle = normalRelativeAngle(getHeading() - angle + 180.0D);
            if (angle > 90.0D)
            {
              angle -= 180.0D;
              dir = -1;
            }
            else if (angle < -90.0D)
            {
              angle += 180.0D;
              dir = -1;
            }

            distance=Math.pow((x-getX())*(x-getX())+(y-getY())*(y-getY()), 0.5);
            setTurnLeft(angle);

            ahead(distance * dir);

        }

        /* 리더 확인 메소드 */
        boolean isLeader(){
            if(getEnergy()>200)
                return true; // 리더
            return false; // 노예

        }

        /* 절대 각도 계산 */
        public double normalAbsoluteHeading(double angle){
          if (angle < 0.0D) {
            return 360.0D + angle % 360.0D;
          }
          return angle % 360.0D;
        }

        /* 상대 각도 계산 */
        public double normalRelativeAngle(double angle){
          if (angle > 180.0D)
            return (angle + 180.0D) % 360.0D - 180.0D;
          if (angle < -180.0D)
            return (angle - 180.0D) % 360.0D + 180.0D;
          return angle;
        }

        /* 변수 반환 함수 */
        int getDodgeCount() {
            return dodgeCount;
        }

        int getHitBullet() {
            return hitBullet;
        }

        int getHitWall() {
            return hitWall;
        }

        int getHitMyBullet() {
            return hitMyBullet;
        }

        int getTeamCount() {
            return teamCount;
        }

        double getBulletBearing() {
            return bulletBearing;
        }

        boolean getLeader() {
            return leader;
        }

        boolean getDodgeMan() {
            return dodgeMan;
        }

    }
  1. 정확히는 단순한 초기화 과정이 아닌 새로 아이디를 만들었다.