Tổng quát về chiến thuật
Do hạng cân của robot lần này khá nhỏ, nên mình chọn chiến thuật như sau:
- Di chuyển:
- Ở trạng thái bình thường, di chuyển ngẫu nhiên.
- Nếu ghi nhận việc robot đối thủ giảm năng lượng, ta mặc định rằng đối thủ đang bắn mình và di chuyển ngược lại với quỹ đạo trước đó để né đạn.
- Nếu chạm vào tường, ta cũng tiến hành di chuyển ngược lại với quỹ đạo trước đó.
- Ngắm bắn: ta mô hình hoá việc ngắm bắn như sau
- Dưới góc nhìn của robot ta, robot địch sẽ di chuyển sang trái hoặc phải ở trước mặt chúng ta (đoạn x)
- Khi scan được robot địch, ta đã có góc α và khoảng cách d từ ta đến địch, việc xác đoạn x dựa vào công thức:
- x = sinα.d
- x có thể dương, âm hoặc bằng 0.
- x thay đổi theo thời gian trận đấu.
Ta mô hình được việc chuyển động của xe tăng địch dựa vào biến x dựa theo thời gian trận đấu trôi qua.
Ở một thời điểm xác định, muốn ngắm bắn chuẩn xác, ta cần dự đoán được đoạn x sẽ thay đổi như thế nào. Cụ thể ở hình minh hoạ sau, ta lấy một sample(mẫu – tức là những dữ liệu gần nhất của đối thủ) và so sánh với dữ liệu đã ghi nhận trước đó. Giả sử ta tìm được đoạn found đúng bằng đoạn sample, ta sẽ có thể dự đoán được đoạn predict cần thiết để ngắm bắn.
Xử lý phía trong
Để mô hình được dữ liệu đoạn x theo thời gian, có rất nhiều cách. Nhưng do hạng cân của robot khá hạn chế(Nano) nên mình chọn cách đưa hết dữ liệu vào trong một string.
Để tìm đoạn found phù hợp thì ta chỉ cần dùng hàm indexOf có sẵn:
public static String enemyHistory = "" + (char) 0 + (char) 0 + (char) 0 + (char) 0 ...;
private final static int OFFSET = 14;
.....
int ri = 30;
int matchPos;
while((matchPos = enemyHistory.indexOf(enemyHistory.substring(0, ri--), OFFSET)) < 0);
Trong đoạn mã này, mình đã kết hợp vài kỹ thuật sau:
- Dùng vòng while để tìm được vị trí khớp matchPos của đoạn found và sample.
- Thu nhỏ dần độ dài của đoạn sample để tìm kết quả tốt hợn (đoạn sample càng dài thì ta càng khó tìm được found, nhưng càng ngắn thì lại càng thiếu chính xác) bằng cách giảm độ lớn biến ri.
- OFFSET dùng để config cho phù hợp sau nhiều trận đấu thử.
- string enemyHistory sẽ được tạo trước với một bộ dữ liệu mẫu (bộ dữ liệu này không có tính hữu dụng quá nhiều, chỉ để chiến thuật có thể chạy).
double rd = e.getBearingRadians();
ri = OFFSET;
rd += getHeadingRadians();
do {
rd += ((short) enemyHistory.charAt(--matchPos)) / (160.0);
} while (--ri > 0);
setTurnGunRightRadians(Utils.normalRelativeAngle(rd - getGunHeadingRadians()));
setFire(2.5);
Đoạn mã này dùng để ngắm và bắn:
- Tính toán góc bắn bắt đầu từ vị trí matchPos trở về(lấy số điểm bằng OFFSET)
- Dùng các hàm dựng sẵn của API robocode để tính toán góc bắn.
- Để độ lớn đạn bằng 2.5 cho mỗi lần bắn.
Ngoài ra, như đã trình bày trong phần chiến thuật, đây là đoạn mã di chuyển(đâm vào tường, bị bắn hoặc ghi nhận robot địch giảm năng lượng):
private static double enemyEnergy;
private static double moveDir = 240;
private static int mode = -1;
public void onScannedRobot(ScannedRobotEvent e) {
...
if (enemyEnergy > (enemyEnergy = e.getEnergy()))
{
setAhead((moveDir *= mode) * (Math.random() + 0.15));
}
...
}
public void onHitWall(HitWallEvent e)
{
setAhead(moveDir = -moveDir);
}
public void onHitByBullet(HitByBulletEvent event) {
mode = -mode;
}
Kết luận
Robot với chiến thuật như của mình sẽ đốt mặt khá tốt với những robot có quỹ đạo di chuyển lặp lại hoặc có quy luật nhất định, nhưng sẽ gặp khó khăn với các robot thông minh hơn(đơn cử như DrustGT) hoặc robot di chuyển không có quy luật. Hoặc nếu robot dùng đạn nhỏ để chặn đạn của mình cho đến khi mình hết năng lượng thì cũng là một cách xử lý khá hay.
Mong mọi người có thể góp ý để robot của mình có thể hoàn thiện hơn.
Cảm ơn đã đọc bài viết!!!
1 bình luận
Good