1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
use crate::raw;
use crate::raw::{DIRECTION_EAST, DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_WEST};
use core::hint::unreachable_unchecked;
use core::mem::MaybeUninit;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Direction {
    North = DIRECTION_NORTH as isize,
    East = DIRECTION_EAST as isize,
    South = DIRECTION_SOUTH as isize,
    West = DIRECTION_WEST as isize,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ObservationItem {
    Food(FoodInfo),
    SnakeHead(SnakeInfo),
    SnakeBody(SnakeInfo),
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Observation {
    /// The amount of damage to a snakes health if the head of a snake
    /// is on top of poison at the end of a tick
    pub poison: u32,

    /// The item at this location
    pub item: Option<ObservationItem>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct FoodInfo {
    /// The amount of health that a snake will gain by eating this food.
    pub health_value: u32,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SnakeInfo {
    pub team_id: u32,
    pub snake_id: u32,
    pub health: u32,
}

/// Get the number of CPU cycles executed per tick. This is constant.
pub fn get_cpu_cycles_per_tick() -> u32 {
    unsafe { raw::get_cpu_cycles_per_tick() }
}

/// Sets the direction of the snake. This is the direction the snake will move next.
pub fn set_direction(direction: Direction) {
    unsafe {
        raw::set_direction(direction as u32);
    }
}

/// Move immediately in the current direction. A snake must move once per tick.
/// If the snake isn't manually moved, it will be automatically called at the end of the tick.
/// Note that this takes effect on the exact CPU cycle it is called. This means that all
/// snakes do not necessarily move at the same time (you can pick when during the tick the move occurs).
pub fn move_snake() {
    unsafe { raw::r#move() };
}

/// A leap is an additional move that can occur at most once every 2 ticks.
/// It must only be called during a tick in which a move has already been performed, and
/// a leap was not performed during the current or previous tick.
/// Performing a leap will consume the last body part of this snake, which will drop on the floor as poison.
pub fn leap() {
    unsafe {
        raw::leap();
    }
}

/// Sleep (do nothing) for all remaining CPU cycles in the current tick.
pub fn sleep_remaining_tick() {
    unsafe {
        raw::sleep_remaining_tick();
    }
}

/// Sleep (do nothing) for a specific number of cycles.
pub fn sleep(cycles: u32) {
    unsafe { raw::sleep(cycles) }
}

/// Get the width of the arena. This is constant and does not change during a game.
pub fn get_arena_width() -> u32 {
    unsafe { raw::get_arena_width() }
}

/// Get the height of the arena. This is constant and does not change during a game.
pub fn get_arena_height() -> u32 {
    unsafe { raw::get_arena_height() }
}

/// Get the (width, height) of the arena. This is constant and does not change during a game.
pub fn get_arena_size() -> (u32, u32) {
    (get_arena_width(), get_arena_height())
}

/// Get the current position of the head of the snake.
pub fn get_current_pos() -> (u32, u32) {
    let mut x = MaybeUninit::<u32>::uninit();
    let mut y = MaybeUninit::<u32>::uninit();
    unsafe {
        raw::get_current_pos(x.as_mut_ptr(), y.as_mut_ptr());
        (x.assume_init(), y.assume_init())
    }
}

/// Get the current tick. Starts at 0.
pub fn get_current_tick() -> u64 {
    unsafe { raw::get_current_tick() }
}

/// Get the number of CPU cycles that have already executed in the current tick.
pub fn get_current_cpu_cycle_in_tick() -> u64 {
    unsafe { raw::get_current_cpu_cycle_in_tick() }
}

/// Kill the snake immediately. All parts of the snake will turn into poison.
pub fn suicide() {
    unsafe { raw::suicide() }
}

/// Logs a message. The maximum length is 50. Any message longer than this will be truncated.
/// A snake can send at most 10 logs per tick. Any additional logs will be ignored.
pub fn speak(msg: &str) {
    unsafe { raw::speak(msg.as_ptr(), msg.len() as u32) }
}

/// If the snake has a length of at least 9, it is eligible to split into two.
/// The snake is split into three parts (the middle is rounded up, the rest down). The first part will remain as
/// the original snake. The middle part will be lost and turn into poison. The end will become a new
/// snake. The snake runtime will be forked, and both new snakes will continue to run independently.
/// You can use `get_id` to determine which snake is now running.
pub fn split() {
    unsafe { raw::split() }
}

/// Get the id of the snake. This is constant, except after a `split`, where the new snake will have a new id.
pub fn get_id() -> u32 {
    unsafe { raw::get_id() }
}

/// Get the id of your team. This is constant.
pub fn get_team_id() -> u32 {
    unsafe { raw::get_team_id() }
}

/// View a certain position in the arena.
pub fn observe(x: u32, y: u32) -> Observation {
    const OUT_TYPE: usize = 0;
    const OUT_TEAM_ID: usize = 1;
    const OUT_SNAKE_ID: usize = 2;
    const OUT_HEALTH: usize = 3;
    const OUT_POISON: usize = 4;

    let mut out = MaybeUninit::<[u32; 5]>::uninit();
    let out = unsafe {
        raw::observe(x, y, out.as_mut_ptr());
        out.assume_init()
    };

    let poison = out[OUT_POISON];
    let item = match out[OUT_TYPE] {
        raw::TYPE_EMPTY => None,
        raw::TYPE_FOOD => Some(ObservationItem::Food(FoodInfo {
            health_value: out[OUT_HEALTH],
        })),
        raw::TYPE_SNAKE_HEAD => Some(ObservationItem::SnakeHead(SnakeInfo {
            team_id: out[OUT_TEAM_ID],
            snake_id: out[OUT_SNAKE_ID],
            health: out[OUT_HEALTH],
        })),
        raw::TYPE_SNAKE_BODY => Some(ObservationItem::SnakeBody(SnakeInfo {
            team_id: out[OUT_TEAM_ID],
            snake_id: out[OUT_SNAKE_ID],
            health: out[OUT_HEALTH],
        })),
        _ => unsafe { unreachable_unchecked() },
    };

    Observation { poison, item }
}

/// Get the length of the snake.
pub fn get_length() -> u32 {
    unsafe { raw::get_length() }
}

/// Get the current health of the snake. The maximum health is 100. The health decreases by 1 each tick.
/// If the health of a snake ever reaches zero, it will die. A snake can gain health by eating food.
/// Food increases health. Poison damages health.
pub fn get_health() -> u32 {
    unsafe { raw::get_health() }
}

/// Get a random value between the min (inclusive) and max (exclusive).
/// The returned value is deterministic based on the current state of the game and the initial seed.
pub fn rand(min: u32, max: u32) -> u32 {
    unsafe { raw::rand(min, max) }
}