Compare commits
No commits in common. "16c78c81aebc470265de79c71f7a342b7caa1f92" and "c5b0319ac4c9d238107e0031746f978de32163c7" have entirely different histories.
16c78c81ae
...
c5b0319ac4
114
Cargo.lock
generated
114
Cargo.lock
generated
@ -26,12 +26,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anes"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "735d4f398ca57cfa2880225c2bf81c3b9af3be5bb22e44ae70118dad38713e84"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.15"
|
||||||
@ -154,7 +148,6 @@ dependencies = [
|
|||||||
name = "battlesnake"
|
name = "battlesnake"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion2",
|
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"iter_tools",
|
"iter_tools",
|
||||||
@ -178,12 +171,6 @@ version = "2.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bpaf"
|
|
||||||
version = "0.9.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d4e5ca9929037866947af4b8b7418124f2ec7c411a8b9ee24e46ad2b8470497"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.17.1"
|
version = "1.17.1"
|
||||||
@ -202,12 +189,6 @@ version = "1.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cast"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.15"
|
version = "1.1.15"
|
||||||
@ -223,33 +204,6 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ciborium"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
|
||||||
dependencies = [
|
|
||||||
"ciborium-io",
|
|
||||||
"ciborium-ll",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ciborium-io"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ciborium-ll"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
|
||||||
dependencies = [
|
|
||||||
"ciborium-io",
|
|
||||||
"half",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clone_dyn_types"
|
name = "clone_dyn_types"
|
||||||
version = "0.23.0"
|
version = "0.23.0"
|
||||||
@ -273,24 +227,6 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "criterion2"
|
|
||||||
version = "1.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d6c2be2af5816b76f45745ccfb9959a9efd1322ae4c88fc3c9b2d6bcdabe5554"
|
|
||||||
dependencies = [
|
|
||||||
"anes",
|
|
||||||
"bpaf",
|
|
||||||
"cast",
|
|
||||||
"ciborium",
|
|
||||||
"num-traits",
|
|
||||||
"oorandom",
|
|
||||||
"rayon",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"walkdir",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -316,12 +252,6 @@ version = "0.8.20"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crunchy"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
@ -584,16 +514,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "half"
|
|
||||||
version = "2.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"crunchy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
@ -906,12 +826,6 @@ version = "1.19.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "oorandom"
|
|
||||||
version = "11.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "4.3.0"
|
version = "4.3.0"
|
||||||
@ -1273,15 +1187,6 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "same-file"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -1681,16 +1586,6 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "walkdir"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
|
||||||
dependencies = [
|
|
||||||
"same-file",
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -1722,15 +1617,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-util"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -26,16 +26,8 @@ enum-iterator = "2.1"
|
|||||||
iter_tools = "0.21"
|
iter_tools = "0.21"
|
||||||
ordered-float = "4.3.0"
|
ordered-float = "4.3.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
# criterion = { version = "0.5.1", features = ["html_reports"] }
|
|
||||||
criterion2 = "1.1.1"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "simulation"
|
|
||||||
harness = false
|
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
use battlesnake::{
|
|
||||||
simulation::{self, SnakeToken},
|
|
||||||
Coord,
|
|
||||||
};
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use rand::{rngs::StdRng, SeedableRng};
|
|
||||||
|
|
||||||
fn random_moves(board: &battlesnake::Board) -> Option<SnakeToken> {
|
|
||||||
let token_map = SnakeToken::from_board(board);
|
|
||||||
let mut board = simulation::Board::from_game_board(board, &token_map, 0, 15, 1, false);
|
|
||||||
let mut rng = StdRng::seed_from_u64(0);
|
|
||||||
|
|
||||||
board.simulate_until(&mut rng, |board| board.alive_snakes() <= 1);
|
|
||||||
|
|
||||||
let winner = board.snakes().next();
|
|
||||||
winner
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_duel_random_moves(c: &mut Criterion) {
|
|
||||||
c.bench_function("duel random moves", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
random_moves(black_box(&battlesnake::Board {
|
|
||||||
height: 11,
|
|
||||||
width: 11,
|
|
||||||
food: vec![Coord { x: 5, y: 5 }],
|
|
||||||
snakes: vec![
|
|
||||||
battlesnake::Battlesnake {
|
|
||||||
id: "1".to_owned(),
|
|
||||||
name: "1".to_owned(),
|
|
||||||
health: 100,
|
|
||||||
body: vec![Coord { x: 5, y: 1 }; 3],
|
|
||||||
head: Coord { x: 5, y: 1 },
|
|
||||||
length: 3,
|
|
||||||
latency: "0".to_owned(),
|
|
||||||
shout: None,
|
|
||||||
squad: String::new(),
|
|
||||||
},
|
|
||||||
battlesnake::Battlesnake {
|
|
||||||
id: "2".to_owned(),
|
|
||||||
name: "2".to_owned(),
|
|
||||||
health: 100,
|
|
||||||
body: vec![Coord { x: 5, y: 9 }; 3],
|
|
||||||
head: Coord { x: 5, y: 9 },
|
|
||||||
length: 3,
|
|
||||||
latency: "0".to_owned(),
|
|
||||||
shout: None,
|
|
||||||
squad: String::new(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
hazards: vec![],
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_standard_random_moves(c: &mut Criterion) {
|
|
||||||
c.bench_function("standard random moves", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
random_moves(black_box(&battlesnake::Board {
|
|
||||||
height: 11,
|
|
||||||
width: 11,
|
|
||||||
food: vec![Coord { x: 5, y: 5 }],
|
|
||||||
snakes: vec![
|
|
||||||
battlesnake::Battlesnake {
|
|
||||||
id: "1".to_owned(),
|
|
||||||
name: "1".to_owned(),
|
|
||||||
health: 100,
|
|
||||||
body: vec![Coord { x: 5, y: 1 }; 3],
|
|
||||||
head: Coord { x: 5, y: 1 },
|
|
||||||
length: 3,
|
|
||||||
latency: "0".to_owned(),
|
|
||||||
shout: None,
|
|
||||||
squad: String::new(),
|
|
||||||
},
|
|
||||||
battlesnake::Battlesnake {
|
|
||||||
id: "2".to_owned(),
|
|
||||||
name: "2".to_owned(),
|
|
||||||
health: 100,
|
|
||||||
body: vec![Coord { x: 5, y: 9 }; 3],
|
|
||||||
head: Coord { x: 5, y: 9 },
|
|
||||||
length: 3,
|
|
||||||
latency: "0".to_owned(),
|
|
||||||
shout: None,
|
|
||||||
squad: String::new(),
|
|
||||||
},
|
|
||||||
battlesnake::Battlesnake {
|
|
||||||
id: "3".to_owned(),
|
|
||||||
name: "3".to_owned(),
|
|
||||||
health: 100,
|
|
||||||
body: vec![Coord { x: 1, y: 5 }; 3],
|
|
||||||
head: Coord { x: 1, y: 5 },
|
|
||||||
length: 3,
|
|
||||||
latency: "0".to_owned(),
|
|
||||||
shout: None,
|
|
||||||
squad: String::new(),
|
|
||||||
},
|
|
||||||
battlesnake::Battlesnake {
|
|
||||||
id: "4".to_owned(),
|
|
||||||
name: "4".to_owned(),
|
|
||||||
health: 100,
|
|
||||||
body: vec![Coord { x: 9, y: 5 }; 3],
|
|
||||||
head: Coord { x: 9, y: 5 },
|
|
||||||
length: 3,
|
|
||||||
latency: "0".to_owned(),
|
|
||||||
shout: None,
|
|
||||||
squad: String::new(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
hazards: vec![],
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
bench_duel_random_moves,
|
|
||||||
bench_standard_random_moves
|
|
||||||
);
|
|
||||||
criterion_main!(benches);
|
|
@ -1,183 +0,0 @@
|
|||||||
use enum_iterator::Sequence;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub mod logic;
|
|
||||||
pub mod simulation;
|
|
||||||
|
|
||||||
pub const MAX_HEALTH: i32 = 100;
|
|
||||||
|
|
||||||
// API and Response Objects
|
|
||||||
// See https://docs.battlesnake.com/api
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Deserialize, Serialize, Sequence,
|
|
||||||
)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum Direction {
|
|
||||||
/// Move left (-x)
|
|
||||||
Left,
|
|
||||||
/// Move up (+y)
|
|
||||||
Up,
|
|
||||||
/// Move right (+x)
|
|
||||||
Right,
|
|
||||||
/// Move down (-y)
|
|
||||||
Down,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct Action {
|
|
||||||
/// In which direction the snake should move
|
|
||||||
pub r#move: Direction,
|
|
||||||
/// Say something to the other snakes
|
|
||||||
#[serde(default, skip_serializing_if = "is_default")]
|
|
||||||
pub shout: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_default<T: Default + PartialEq>(value: &T) -> bool {
|
|
||||||
*value == T::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
pub struct Game {
|
|
||||||
/// A unique identifier for this Game
|
|
||||||
pub id: String,
|
|
||||||
/// Information about the ruleset being used to run this game
|
|
||||||
pub ruleset: Ruleset,
|
|
||||||
/// The name of the map being played on.
|
|
||||||
pub map: String,
|
|
||||||
/// How much time your snake has to respond to requests for this Game
|
|
||||||
pub timeout: u32,
|
|
||||||
/// The source of this game.
|
|
||||||
///
|
|
||||||
/// One of:
|
|
||||||
/// - "tournament"
|
|
||||||
/// - "league"
|
|
||||||
/// - "arena"
|
|
||||||
/// - "challenge"
|
|
||||||
/// - "custom"
|
|
||||||
///
|
|
||||||
/// The values may change.
|
|
||||||
pub source: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct Ruleset {
|
|
||||||
/// Name of the ruleset being used to run this game.
|
|
||||||
pub name: String,
|
|
||||||
/// The release version of the [Rules](https://github.com/BattlesnakeOfficial/rules) module used in this game.
|
|
||||||
pub version: String,
|
|
||||||
/// A collection of specific settings being used by the current game that control how the rules
|
|
||||||
/// are applied.
|
|
||||||
pub settings: RulesetSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct RulesetSettings {
|
|
||||||
/// Percentage chance of spawning a new food every round.
|
|
||||||
#[serde(rename = "foodSpawnChance")]
|
|
||||||
pub food_spawn_chance: u8,
|
|
||||||
/// Minimum food to keep on the board every turn.
|
|
||||||
#[serde(rename = "minimumFood")]
|
|
||||||
pub minimum_food: u8,
|
|
||||||
/// Health damage a snake will take when ending its turn in a hazard. This stacks on top of the
|
|
||||||
/// regular 1 damage a snake takes per turn.
|
|
||||||
#[serde(rename = "hazardDamagePerTurn")]
|
|
||||||
pub hazard_damage_per_turn: u8,
|
|
||||||
/// rules for the royale mode
|
|
||||||
pub royale: RulesetRoyale,
|
|
||||||
/// rules for the squad mode
|
|
||||||
pub squad: RulesetSquad,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct RulesetRoyale {
|
|
||||||
/// The number of turns between generating new hazards (shrinking the safe board space).
|
|
||||||
#[serde(rename = "shrinkEveryNTurns")]
|
|
||||||
pub shrink_every_n_turns: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct RulesetSquad {
|
|
||||||
/// Allow members of the same squad to move over each other without dying.
|
|
||||||
#[serde(rename = "allowBodyCollisions")]
|
|
||||||
pub allow_body_collisions: bool,
|
|
||||||
/// All squad members are eliminated when one is eliminated.
|
|
||||||
#[serde(rename = "sharedElimination")]
|
|
||||||
pub shared_elimination: bool,
|
|
||||||
/// All squad members share health.
|
|
||||||
#[serde(rename = "sharedHealth")]
|
|
||||||
pub shared_health: bool,
|
|
||||||
/// All squad members share length.
|
|
||||||
#[serde(rename = "sharedLength")]
|
|
||||||
pub shared_length: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct Board {
|
|
||||||
/// The number of rows in the y-axis of the game board.
|
|
||||||
pub height: i32,
|
|
||||||
/// The number of columns in the x-axis of the game board.
|
|
||||||
pub width: i32,
|
|
||||||
/// Array of coordinates representing food locations on the game board.
|
|
||||||
pub food: Vec<Coord>,
|
|
||||||
/// Array of Battlesnakes representing all Battlesnakes remaining on the game board (including
|
|
||||||
/// yourself if you haven't been eliminated).
|
|
||||||
pub snakes: Vec<Battlesnake>,
|
|
||||||
/// Array of coordinates representing hazardous locations on the game board.
|
|
||||||
pub hazards: Vec<Coord>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct Battlesnake {
|
|
||||||
/// Unique identifier for this Battlesnake in the context of the current Game
|
|
||||||
pub id: String,
|
|
||||||
/// Name given to this Battlesnake by its author
|
|
||||||
pub name: String,
|
|
||||||
/// Health value of this Battlesnake, between 0 and 100
|
|
||||||
pub health: i32,
|
|
||||||
/// Array of coordinates representing this Battlesnake's location on the game board. This array
|
|
||||||
/// is ordered from head to tail
|
|
||||||
pub body: Vec<Coord>,
|
|
||||||
/// Coordinates for this Battlesnake's head. Equivalent to the first element of the body array.
|
|
||||||
pub head: Coord,
|
|
||||||
/// Length of this Battlesnake from head to tail. Equivalent to the length of the body array.
|
|
||||||
pub length: i32,
|
|
||||||
/// The previous response time of this Battlesnake, in milliseconds. If the Battlesnake timed
|
|
||||||
/// out and failed to respond, the game timeout will be returned.
|
|
||||||
pub latency: String,
|
|
||||||
/// Message shouted by this Battlesnake on the previous turn.
|
|
||||||
pub shout: Option<String>,
|
|
||||||
/// The squad that the Battlesnake belongs to. Used to identify squad members in Squad Mode
|
|
||||||
/// games.
|
|
||||||
pub squad: String,
|
|
||||||
// /// The collection of customizations that control how this Battlesnake is displayed.
|
|
||||||
// customizations: {color, head, tail}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deserialize, Serialize)]
|
|
||||||
pub struct Coord {
|
|
||||||
pub x: i32,
|
|
||||||
pub y: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Coord {
|
|
||||||
#[must_use]
|
|
||||||
pub 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)]
|
|
||||||
pub struct GameState {
|
|
||||||
pub game: Game,
|
|
||||||
pub turn: i32,
|
|
||||||
pub board: Board,
|
|
||||||
pub you: Battlesnake,
|
|
||||||
}
|
|
@ -19,7 +19,6 @@ use std::{
|
|||||||
|
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use rand::thread_rng;
|
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -206,9 +205,7 @@ impl Node {
|
|||||||
) -> Result<Option<SnakeToken>, DeadlineError> {
|
) -> Result<Option<SnakeToken>, DeadlineError> {
|
||||||
let winner = if self.statistic.played == 0 {
|
let winner = if self.statistic.played == 0 {
|
||||||
// We didn't simulate a game for this node yet. Do that
|
// We didn't simulate a game for this node yet. Do that
|
||||||
board.simulate_until(&mut thread_rng(), |board| {
|
board.simulate_until(|board| board.alive_snakes() <= 1 || Instant::now() >= *deadline);
|
||||||
board.alive_snakes() <= 1 || Instant::now() >= *deadline
|
|
||||||
});
|
|
||||||
if Instant::now() >= *deadline {
|
if Instant::now() >= *deadline {
|
||||||
return Err(DeadlineError);
|
return Err(DeadlineError);
|
||||||
}
|
}
|
||||||
@ -242,7 +239,7 @@ impl Node {
|
|||||||
if Instant::now() >= *deadline {
|
if Instant::now() >= *deadline {
|
||||||
return Err(DeadlineError);
|
return Err(DeadlineError);
|
||||||
}
|
}
|
||||||
board.simulate_actions(&actions, &mut thread_rng());
|
board.simulate_actions(&actions);
|
||||||
let winner = self
|
let winner = self
|
||||||
.childs
|
.childs
|
||||||
.entry(actions.clone())
|
.entry(actions.clone())
|
||||||
@ -290,7 +287,7 @@ impl Node {
|
|||||||
.snakes()
|
.snakes()
|
||||||
.filter_map(|snake| Some((snake, board.snake_length(snake)?)))
|
.filter_map(|snake| Some((snake, board.snake_length(snake)?)))
|
||||||
.collect();
|
.collect();
|
||||||
board.simulate_until(&mut thread_rng(), |board| {
|
board.simulate_until(|board| {
|
||||||
if Instant::now() >= *deadline {
|
if Instant::now() >= *deadline {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -336,7 +333,7 @@ impl Node {
|
|||||||
if Instant::now() >= *deadline {
|
if Instant::now() >= *deadline {
|
||||||
return Err(DeadlineError);
|
return Err(DeadlineError);
|
||||||
}
|
}
|
||||||
board.simulate_actions(&actions, &mut thread_rng());
|
board.simulate_actions(&actions);
|
||||||
let lengths = self
|
let lengths = self
|
||||||
.childs
|
.childs
|
||||||
.entry(actions.clone())
|
.entry(actions.clone())
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![allow(clippy::needless_pass_by_value)]
|
#![allow(clippy::needless_pass_by_value)]
|
||||||
|
|
||||||
use battlesnake::{logic, Action, GameState};
|
use enum_iterator::Sequence;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
fairing::AdHoc,
|
fairing::AdHoc,
|
||||||
@ -10,9 +10,190 @@ use rocket::{
|
|||||||
serde::{json::Json, Deserialize},
|
serde::{json::Json, Deserialize},
|
||||||
tokio::task,
|
tokio::task,
|
||||||
};
|
};
|
||||||
|
use serde::Serialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
mod logic;
|
||||||
|
mod simulation;
|
||||||
|
|
||||||
|
const MAX_HEALTH: i32 = 100;
|
||||||
|
|
||||||
|
// API and Response Objects
|
||||||
|
// See https://docs.battlesnake.com/api
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Deserialize, Serialize, Sequence,
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
enum Direction {
|
||||||
|
/// Move left (-x)
|
||||||
|
Left,
|
||||||
|
/// Move up (+y)
|
||||||
|
Up,
|
||||||
|
/// Move right (+x)
|
||||||
|
Right,
|
||||||
|
/// Move down (-y)
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
struct Action {
|
||||||
|
/// In which direction the snake should move
|
||||||
|
r#move: Direction,
|
||||||
|
/// Say something to the other snakes
|
||||||
|
#[serde(default, skip_serializing_if = "is_default")]
|
||||||
|
shout: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_default<T: Default + PartialEq>(value: &T) -> bool {
|
||||||
|
*value == T::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
struct Game {
|
||||||
|
/// A unique identifier for this Game
|
||||||
|
id: String,
|
||||||
|
/// Information about the ruleset being used to run this game
|
||||||
|
ruleset: Ruleset,
|
||||||
|
/// The name of the map being played on.
|
||||||
|
map: String,
|
||||||
|
/// How much time your snake has to respond to requests for this Game
|
||||||
|
timeout: u32,
|
||||||
|
/// The source of this game.
|
||||||
|
///
|
||||||
|
/// One of:
|
||||||
|
/// - "tournament"
|
||||||
|
/// - "league"
|
||||||
|
/// - "arena"
|
||||||
|
/// - "challenge"
|
||||||
|
/// - "custom"
|
||||||
|
///
|
||||||
|
/// The values may change.
|
||||||
|
source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
struct Ruleset {
|
||||||
|
/// Name of the ruleset being used to run this game.
|
||||||
|
name: String,
|
||||||
|
/// The release version of the [Rules](https://github.com/BattlesnakeOfficial/rules) module used in this game.
|
||||||
|
version: String,
|
||||||
|
/// A collection of specific settings being used by the current game that control how the rules
|
||||||
|
/// are applied.
|
||||||
|
settings: RulesetSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
struct RulesetSettings {
|
||||||
|
/// Percentage chance of spawning a new food every round.
|
||||||
|
#[serde(rename = "foodSpawnChance")]
|
||||||
|
food_spawn_chance: u8,
|
||||||
|
/// Minimum food to keep on the board every turn.
|
||||||
|
#[serde(rename = "minimumFood")]
|
||||||
|
minimum_food: u8,
|
||||||
|
/// Health damage a snake will take when ending its turn in a hazard. This stacks on top of the
|
||||||
|
/// regular 1 damage a snake takes per turn.
|
||||||
|
#[serde(rename = "hazardDamagePerTurn")]
|
||||||
|
hazard_damage_per_turn: u8,
|
||||||
|
/// rules for the royale mode
|
||||||
|
royale: RulesetRoyale,
|
||||||
|
/// rules for the squad mode
|
||||||
|
squad: RulesetSquad,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
struct RulesetRoyale {
|
||||||
|
/// The number of turns between generating new hazards (shrinking the safe board space).
|
||||||
|
#[serde(rename = "shrinkEveryNTurns")]
|
||||||
|
shrink_every_n_turns: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
struct RulesetSquad {
|
||||||
|
/// Allow members of the same squad to move over each other without dying.
|
||||||
|
#[serde(rename = "allowBodyCollisions")]
|
||||||
|
allow_body_collisions: bool,
|
||||||
|
/// All squad members are eliminated when one is eliminated.
|
||||||
|
#[serde(rename = "sharedElimination")]
|
||||||
|
shared_elimination: bool,
|
||||||
|
/// All squad members share health.
|
||||||
|
#[serde(rename = "sharedHealth")]
|
||||||
|
shared_health: bool,
|
||||||
|
/// All squad members share length.
|
||||||
|
#[serde(rename = "sharedLength")]
|
||||||
|
shared_length: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
struct Board {
|
||||||
|
/// The number of rows in the y-axis of the game board.
|
||||||
|
height: i32,
|
||||||
|
/// The number of columns in the x-axis of the game board.
|
||||||
|
width: i32,
|
||||||
|
/// Array of coordinates representing food locations on the game board.
|
||||||
|
food: Vec<Coord>,
|
||||||
|
/// Array of Battlesnakes representing all Battlesnakes remaining on the game board (including
|
||||||
|
/// yourself if you haven't been eliminated).
|
||||||
|
snakes: Vec<Battlesnake>,
|
||||||
|
/// Array of coordinates representing hazardous locations on the game board.
|
||||||
|
hazards: Vec<Coord>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
struct Battlesnake {
|
||||||
|
/// Unique identifier for this Battlesnake in the context of the current Game
|
||||||
|
id: String,
|
||||||
|
/// Name given to this Battlesnake by its author
|
||||||
|
name: String,
|
||||||
|
/// Health value of this Battlesnake, between 0 and 100
|
||||||
|
health: i32,
|
||||||
|
/// Array of coordinates representing this Battlesnake's location on the game board. This array
|
||||||
|
/// is ordered from head to tail
|
||||||
|
body: Vec<Coord>,
|
||||||
|
/// Coordinates for this Battlesnake's head. Equivalent to the first element of the body array.
|
||||||
|
head: Coord,
|
||||||
|
/// Length of this Battlesnake from head to tail. Equivalent to the length of the body array.
|
||||||
|
length: i32,
|
||||||
|
/// The previous response time of this Battlesnake, in milliseconds. If the Battlesnake timed
|
||||||
|
/// out and failed to respond, the game timeout will be returned.
|
||||||
|
latency: String,
|
||||||
|
/// Message shouted by this Battlesnake on the previous turn.
|
||||||
|
shout: Option<String>,
|
||||||
|
/// The squad that the Battlesnake belongs to. Used to identify squad members in Squad Mode
|
||||||
|
/// games.
|
||||||
|
squad: String,
|
||||||
|
// /// The collection of customizations that control how this Battlesnake is displayed.
|
||||||
|
// customizations: {color, head, tail}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deserialize, Serialize)]
|
||||||
|
struct Coord {
|
||||||
|
x: 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)]
|
||||||
|
struct GameState {
|
||||||
|
game: Game,
|
||||||
|
turn: i32,
|
||||||
|
board: Board,
|
||||||
|
you: Battlesnake,
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn handle_index() -> Json<Value> {
|
fn handle_index() -> Json<Value> {
|
||||||
Json(logic::info())
|
Json(logic::info())
|
||||||
|
@ -86,10 +86,6 @@ impl Board {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn turn(&self) -> i32 {
|
|
||||||
self.turn
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub const fn spaces(&self) -> usize {
|
pub const fn spaces(&self) -> usize {
|
||||||
self.height as usize * self.width as usize
|
self.height as usize * self.width as usize
|
||||||
@ -107,11 +103,7 @@ impl Board {
|
|||||||
self.snakes.get(&token).map(|snake| snake.body.len())
|
self.snakes.get(&token).map(|snake| snake.body.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simulate_actions(
|
pub fn simulate_actions(&mut self, actions: &BTreeMap<SnakeToken, Direction>) {
|
||||||
&mut self,
|
|
||||||
actions: &BTreeMap<SnakeToken, Direction>,
|
|
||||||
rng: &mut impl Rng,
|
|
||||||
) {
|
|
||||||
// move snakes
|
// move snakes
|
||||||
for (token, snake) in &mut self.snakes {
|
for (token, snake) in &mut self.snakes {
|
||||||
snake.perform_action(actions.get(token).copied().unwrap_or(Direction::Up));
|
snake.perform_action(actions.get(token).copied().unwrap_or(Direction::Up));
|
||||||
@ -160,7 +152,7 @@ impl Board {
|
|||||||
|
|
||||||
// spawn new food
|
// spawn new food
|
||||||
if self.food.len() < usize::from(self.min_food)
|
if self.food.len() < usize::from(self.min_food)
|
||||||
|| rng.gen_ratio(u32::from(self.food_chance), 100)
|
|| rand::thread_rng().gen_ratio(u32::from(self.food_chance), 100)
|
||||||
{
|
{
|
||||||
let free_fields = (0..self.width)
|
let free_fields = (0..self.width)
|
||||||
.flat_map(|x| (0..self.height).map(move |y| Coord { x, y }))
|
.flat_map(|x| (0..self.height).map(move |y| Coord { x, y }))
|
||||||
@ -172,7 +164,7 @@ impl Board {
|
|||||||
.any(|body| body == coord)
|
.any(|body| body == coord)
|
||||||
})
|
})
|
||||||
.filter(|coord| self.food.contains(coord));
|
.filter(|coord| self.food.contains(coord));
|
||||||
if let Some(field) = free_fields.choose(rng) {
|
if let Some(field) = free_fields.choose(&mut rand::thread_rng()) {
|
||||||
self.food.insert(field);
|
self.food.insert(field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +172,7 @@ impl Board {
|
|||||||
self.turn += 1;
|
self.turn += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simulate_until(&mut self, rng: &mut impl Rng, mut exit: impl FnMut(&Self) -> bool) {
|
pub fn simulate_until(&mut self, mut exit: impl FnMut(&Self) -> bool) {
|
||||||
while !exit(self) {
|
while !exit(self) {
|
||||||
let actions = self
|
let actions = self
|
||||||
.possible_actions()
|
.possible_actions()
|
||||||
@ -188,11 +180,15 @@ impl Board {
|
|||||||
.map(|(token, actions)| {
|
.map(|(token, actions)| {
|
||||||
(
|
(
|
||||||
*token,
|
*token,
|
||||||
actions.iter().choose(rng).copied().unwrap_or(Direction::Up),
|
actions
|
||||||
|
.iter()
|
||||||
|
.choose(&mut rand::thread_rng())
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(Direction::Up),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.simulate_actions(&actions, rng);
|
self.simulate_actions(&actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user