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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// vim: noet

use std::mem::size_of;

type IpcReal = f32;
type IpcGuid = u64;

/// IPC representation of one's own snake and current world parameters.
#[repr(C)]
#[repr(align(4))]
pub struct IpcSelfInfo {
    /// Radius of your snake's segments
    pub segment_radius: IpcReal,
    /// Your Snake's current mass
    pub mass: IpcReal,
    /// Radius around your snake's head in which you can see food and segments
    pub sight_radius: IpcReal,
    /// Radius around your snake's head in which food is consumed.
    pub consume_radius: IpcReal,

    /// Frame number when your snake was spawned
    pub start_frame: u32,
    /// Current frame number
    pub current_frame: u32,

    /// Distance per step
    pub speed: IpcReal,
    /// Maximum direction change in this step
    pub max_step_angle: IpcReal,

    /// Amount of "naturally" spawned food your snake consumed
    pub consumed_natural_food: IpcReal,
    /// Amount of food you consumed and that was created from snakes you killed
    pub consumed_food_hunted_by_self: IpcReal,
    /// Amount of food you consumed and that was created from snakes others killed (carrion)
    pub consumed_food_hunted_by_others: IpcReal,
}

/**
 * IPC representation of the static server and world configuration.
 *
 * Please note that this struct is filled once before bot startup, so if you
 * (accidentally) modify them, they stay that way until your bot restarts.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcServerConfig {
    /// Number of steps a snake moves per frame while boosting
    pub snake_boost_steps: u32,
    /// Multiplied with your segment radius to determine the inner turn radius
    pub snake_turn_radius_factor: IpcReal,

    /// Pull-together factor (determines how fast segments move to the center of a loop)
    pub snake_pull_factor: IpcReal,

    /// how much of a snake's mass is converted to food when it dies
    pub snake_conversion_factor: IpcReal,

    /// segment distance = (mass * factor)^exponent
    pub snake_segment_distance_factor: IpcReal,
    /// segment distance = (mass * factor)^exponent
    pub snake_segment_distance_exponent: IpcReal,

    /// consume range multiplier (multiplied with segment radius)
    pub snake_consume_range: IpcReal,

    /// Multiplied with the snakes mass to determine how much mass is lost per frame while boosting
    pub snake_boost_loss_factor: IpcReal,
    /// This part of your mass is dropped every frame (to limit snake growth)
    pub snake_survival_loss_factor: IpcReal,

    /// Mass below which a snake dies through starvation
    pub snake_self_kill_mass_threshold: IpcReal,

    /// Food decays by this value each frame
    pub food_decay_step: IpcReal,

    /// How many log messages you can send per frame
    pub log_credits_per_frame: IpcReal,
    /// How many log messages you can send right after startup
    pub log_initial_credits: IpcReal,
    /// You can send at most this many messages in a row
    pub log_max_credits: IpcReal,
}

/**
 * IPC representation of a food particle.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcFoodInfo {
    /// Relative position X in world orientation
    pub x: IpcReal,
    /// Relative position Y in world orientation
    pub y: IpcReal,
    /// Food value
    pub val: IpcReal,
    /// Direction angle relative to your heading (range -π to +π)
    pub dir: IpcReal,
    /// Distance measured from the center of your head
    pub dist: IpcReal,
}

/**
 * IPC representation of a bot.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcBotInfo {
    /// Bot ID
    pub bot_id: IpcGuid,
    /// Bot name (the beginning of it, at least)
    pub bot_name: [u8; 64],
}

/**
 * IPC representation of a snake segment.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcSegmentInfo {
    /// Relative position X in world orientation
    pub x: IpcReal,
    /// Relative position Y in world orientation
    pub y: IpcReal,
    /// Segment radius
    pub r: IpcReal,
    /// Direction angle relative to your heading (range -π to +π)
    pub dir: IpcReal,
    /// Distance between the center of your head and the segment's center
    pub dist: IpcReal,
    /// Segment number starting from head (idx == 0)
    pub idx: u32,
    /// Bot ID
    pub bot_id: IpcGuid,
    /// True if this segment belongs to ones own snake
    pub is_self: bool,
}

/**
 * IPC representation of a color value.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcColor {
    /// Red channel (0-255)
    pub r: u8,
    /// Green channel (0-255)
    pub g: u8,
    /// Blue channel (0-255)
    pub b: u8,
}

pub const IPC_FOOD_MAX_BYTES: usize = 1024 * 1024;
pub const IPC_FOOD_MAX_COUNT: usize = IPC_FOOD_MAX_BYTES / size_of::<IpcFoodInfo>();

pub const IPC_BOT_MAX_COUNT: usize = 1024;
pub const IPC_BOT_MAX_BYTES: usize = IPC_BOT_MAX_COUNT * size_of::<IpcBotInfo>();

pub const IPC_SEGMENT_MAX_BYTES: usize = 1024 * 1024;
pub const IPC_SEGMENT_MAX_COUNT: usize = IPC_SEGMENT_MAX_BYTES / size_of::<IpcSegmentInfo>();

pub const IPC_COLOR_MAX_COUNT: usize = 1024;
pub const IPC_COLOR_MAX_BYTES: usize = IPC_COLOR_MAX_COUNT * size_of::<IpcColor>();

pub const IPC_LOG_MAX_BYTES: usize = 1024;

/// Space for persistent data (in bytes)
pub const IPC_PERSISTENT_MAX_BYTES: usize = 4096;

/**
 * Shared memory structure.
 *
 * This structure represents the contents of the memory the bot shares with the
 * gameserver.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcSharedMemory {
    /// Information about the world and server configuration.
    pub server_config: IpcServerConfig,

    /// Information about your snake (updated every frame).
    pub self_info: IpcSelfInfo,

    /// Number of items used in foodInfo.
    pub food_count: u32,
    /// List of food items seen by the snake.
    pub food_info: [IpcFoodInfo; IPC_FOOD_MAX_COUNT],

    /// Number of items used in botInfo.
    pub bot_count: u32,
    /// List of bots related to segments in segmentInfo.
    pub bot_info: [IpcBotInfo; IPC_BOT_MAX_COUNT],

    /// Number of items used in segmentInfo.
    pub segment_count: u32,
    /// List of segments seen by the snake.
    pub segment_info: [IpcSegmentInfo; IPC_SEGMENT_MAX_COUNT],

    /// Number of items used in colors.
    pub color_count: u32,
    /// Colors to set for this snake.
    pub colors: [IpcColor; IPC_COLOR_MAX_COUNT],

    /// Log data for the current frame. May contain multiple lines.
    pub log_data: [u8; IPC_LOG_MAX_BYTES],

    /// Select a face for your snake (not used yet).
    pub face_id: u32,
    /// Select a dog tag for your snake (not used yet).
    pub dog_tag_id: u32,

    /// Persistent data: will be saved after your snake dies and restored when it respawns
    pub persistent_data: [u8; IPC_PERSISTENT_MAX_BYTES],
}

pub const IPC_SHARED_MEMORY_BYTES: usize = size_of::<IpcSharedMemory>();

/*
 * Communication structures.
 */

#[repr(C)]
#[repr(align(4))]
#[derive(FromPrimitive, ToPrimitive)]
pub enum IpcRequestType {
    /// Bot initialization request. Sent once after bot startup.
    Init = 0,
    /// Bot step request. Sent every frame.
    Step = 1,
}

/**
 * A request to the bot by the gameserver.
 *
 * Sent by the gameserver on the control socket.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcRequest {
    pub request_type: IpcRequestType,
}

/**
 * The response type sent from the bot to the gameserver.
 */
#[repr(C)]
#[repr(align(4))]
#[derive(FromPrimitive, ToPrimitive)]
pub enum IpcResponseType {
    Ok = 0,
    Error = 1,
}

/**
 * The response structure sent if IpcRequest::request_type was set to Step.
 */
#[repr(C)]
#[repr(align(4))]
#[derive(Clone, Copy)]
pub struct IpcStepResponse {
    /// Direction change in this frame (radians, -π to +π).
    pub delta_angle: IpcReal,
    /// Set to true to boost.
    pub boost: bool,
}

// FIXME! This should actually be a union, which makes things complicated in Rust. As there is
// currently only one entry anyway, we keep ResponseData as a struct and worry about the union
// later.
#[repr(C)]
#[repr(align(4))]
pub union ResponseData {
    pub step: IpcStepResponse,
}

/**
 * Response of the Bot.
 *
 * Sent by the bot on the control socket.
 */
#[repr(C)]
#[repr(align(4))]
pub struct IpcResponse {
    pub response_type: IpcResponseType,

    pub data: ResponseData,
}