advanced move safety check
This commit is contained in:
parent
c301813dc9
commit
bed2b1be81
21
Cargo.lock
generated
21
Cargo.lock
generated
@ -148,6 +148,7 @@ dependencies = [
|
|||||||
name = "battlesnake"
|
name = "battlesnake"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"enum-iterator",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand",
|
||||||
@ -275,6 +276,26 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-iterator"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635"
|
||||||
|
dependencies = [
|
||||||
|
"enum-iterator-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-iterator-derive"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_filter"
|
name = "env_filter"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -22,3 +22,4 @@ serde_json = "1.0.59"
|
|||||||
log = "0.4.0"
|
log = "0.4.0"
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
|
enum-iterator = "2.1"
|
||||||
|
@ -13,9 +13,52 @@
|
|||||||
use log::info;
|
use log::info;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{Battlesnake, Board, Coord, Direction, Game, Move};
|
use crate::{Battlesnake, Board, Direction, Game, Move};
|
||||||
|
|
||||||
|
impl Battlesnake {
|
||||||
|
/// Check if moving the snake in `direction` is safe.
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_direction_safe(&self, direction: Direction, game: &Game, board: &Board) -> bool {
|
||||||
|
let target = self.head.move_to(direction);
|
||||||
|
|
||||||
|
// check if target is out of bounds
|
||||||
|
if !((0..board.width).contains(&target.x) && (0..board.height).contains(&target.y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target is inside a snake
|
||||||
|
if board
|
||||||
|
.snakes
|
||||||
|
.iter()
|
||||||
|
.filter(|snake| {
|
||||||
|
!(game.ruleset.settings.squad.allow_body_collisions && self.squad == snake.squad)
|
||||||
|
})
|
||||||
|
.flat_map(|snake| snake.body.iter())
|
||||||
|
.any(|&coord| target == coord)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a bigger snake could move to this square
|
||||||
|
if board
|
||||||
|
.snakes
|
||||||
|
.iter()
|
||||||
|
.filter(|snake| snake.id != self.id && self.length < snake.length)
|
||||||
|
.flat_map(|snake| enum_iterator::all().map(|dir| snake.head.move_to(dir)))
|
||||||
|
.any(|coord| target == coord)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for hazards
|
||||||
|
if board.hazards.iter().any(|&coord| target == coord) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// info is called when you create your Battlesnake on play.battlesnake.com
|
// info is called when you create your Battlesnake on play.battlesnake.com
|
||||||
// and controls your Battlesnake's appearance
|
// and controls your Battlesnake's appearance
|
||||||
@ -45,87 +88,15 @@ pub fn end(_game: &Game, _turn: i32, _board: &Board, _you: &Battlesnake) {
|
|||||||
// move is called on every turn and returns your next move
|
// move is called on every turn and returns your next move
|
||||||
// Valid moves are "up", "down", "left", or "right"
|
// Valid moves are "up", "down", "left", or "right"
|
||||||
// See https://docs.battlesnake.com/api/example-move for available data
|
// See https://docs.battlesnake.com/api/example-move for available data
|
||||||
pub fn get_move(_game: &Game, turn: i32, board: &Board, you: &Battlesnake) -> Move {
|
pub fn get_move(game: &Game, turn: i32, board: &Board, you: &Battlesnake) -> Move {
|
||||||
use Direction::{Down, Left, Right, Up};
|
let safe_moves = enum_iterator::all()
|
||||||
|
.filter(|&direction| you.is_direction_safe(direction, game, board))
|
||||||
let mut is_move_safe: HashMap<_, _> =
|
|
||||||
vec![(Up, true), (Down, true), (Left, true), (Right, true)]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// We've included code to prevent your Battlesnake from moving backwards
|
|
||||||
let my_head = &you.body[0]; // Coordinates of your head
|
|
||||||
let my_neck = &you.body[1]; // Coordinates of your "neck"
|
|
||||||
|
|
||||||
if my_neck.x < my_head.x {
|
|
||||||
// Neck is left of head, don't move left
|
|
||||||
is_move_safe.insert(Left, false);
|
|
||||||
} else if my_neck.x > my_head.x {
|
|
||||||
// Neck is right of head, don't move right
|
|
||||||
is_move_safe.insert(Right, false);
|
|
||||||
} else if my_neck.y < my_head.y {
|
|
||||||
// Neck is below head, don't move down
|
|
||||||
is_move_safe.insert(Down, false);
|
|
||||||
} else if my_neck.y > my_head.y {
|
|
||||||
// Neck is above head, don't move up
|
|
||||||
is_move_safe.insert(Up, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Step 1 - Prevent your Battlesnake from moving out of bounds
|
|
||||||
if my_head.x == 0 {
|
|
||||||
is_move_safe.insert(Left, false);
|
|
||||||
}
|
|
||||||
if my_head.x == board.width - 1 {
|
|
||||||
is_move_safe.insert(Right, false);
|
|
||||||
}
|
|
||||||
if my_head.y == 0 {
|
|
||||||
is_move_safe.insert(Down, false);
|
|
||||||
}
|
|
||||||
if my_head.y == board.height - 1 {
|
|
||||||
is_move_safe.insert(Up, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Step 2 - Prevent your Battlesnake from colliding with itself
|
|
||||||
if you.body.contains(&Coord {
|
|
||||||
x: my_head.x - 1,
|
|
||||||
y: my_head.y,
|
|
||||||
}) {
|
|
||||||
is_move_safe.insert(Left, false);
|
|
||||||
}
|
|
||||||
if you.body.contains(&Coord {
|
|
||||||
x: my_head.x + 1,
|
|
||||||
y: my_head.y,
|
|
||||||
}) {
|
|
||||||
is_move_safe.insert(Right, false);
|
|
||||||
}
|
|
||||||
if you.body.contains(&Coord {
|
|
||||||
x: my_head.x,
|
|
||||||
y: my_head.y - 1,
|
|
||||||
}) {
|
|
||||||
is_move_safe.insert(Down, false);
|
|
||||||
}
|
|
||||||
if you.body.contains(&Coord {
|
|
||||||
x: my_head.x,
|
|
||||||
y: my_head.y + 1,
|
|
||||||
}) {
|
|
||||||
is_move_safe.insert(Up, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Step 3 - Prevent your Battlesnake from colliding with other Battlesnakes
|
|
||||||
// let opponents = &board.snakes;
|
|
||||||
|
|
||||||
// Are there any safe moves left?
|
|
||||||
let safe_moves = is_move_safe
|
|
||||||
.into_iter()
|
|
||||||
.filter(|&(_, v)| v)
|
|
||||||
.map(|(k, _)| k)
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Choose a random move from the safe ones
|
// Choose a random move from the safe ones
|
||||||
let chosen = safe_moves.choose(&mut rand::thread_rng()).unwrap_or(&Up);
|
let chosen = safe_moves
|
||||||
|
.choose(&mut rand::thread_rng())
|
||||||
// TODO: Step 4 - Move towards food instead of random, to regain health and survive longer
|
.unwrap_or(&Direction::Up);
|
||||||
// let food = &board.food;
|
|
||||||
|
|
||||||
info!("MOVE {}: {:?}", turn, chosen);
|
info!("MOVE {}: {:?}", turn, chosen);
|
||||||
Move {
|
Move {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::needless_pass_by_value)]
|
#![allow(clippy::needless_pass_by_value)]
|
||||||
|
|
||||||
|
use enum_iterator::Sequence;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rocket::fairing::AdHoc;
|
use rocket::fairing::AdHoc;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
@ -14,7 +15,7 @@ mod logic;
|
|||||||
// API and Response Objects
|
// API and Response Objects
|
||||||
// See https://docs.battlesnake.com/api
|
// See https://docs.battlesnake.com/api
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Deserialize, Serialize, Sequence)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
/// Move left (-x)
|
/// Move left (-x)
|
||||||
#[serde(rename = "left")]
|
#[serde(rename = "left")]
|
||||||
@ -165,6 +166,18 @@ pub struct Coord {
|
|||||||
y: i32,
|
y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Coord {
|
||||||
|
const fn move_to(mut self, direction: Direction) -> Self {
|
||||||
|
match direction {
|
||||||
|
Direction::Left => self.x -= 1,
|
||||||
|
Direction::Up => self.y += 1,
|
||||||
|
Direction::Right => self.x += 1,
|
||||||
|
Direction::Down => self.y -= 1,
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct GameState {
|
pub struct GameState {
|
||||||
game: Game,
|
game: Game,
|
||||||
|
Loading…
Reference in New Issue
Block a user