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
|
//! Data structures for the game
#![allow(unused)]
use crate::io::Io;
use async_std::sync::Arc;
use rand::seq::SliceRandom;
use rand::thread_rng;
use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32};
pub type NodeId = usize;
/// A node is a computer on the network graph
///
/// It's owned by a player, and has some upgrade state, as well as
/// base stats.
#[derive(Serialize, Deserialize)]
pub struct Node {
/// Each node has a unique ID by which it's addressed
pub id: NodeId,
/// The current health
pub health: AtomicU32,
/// The max health
pub max_health: AtomicU32,
/// The owner of this node
pub owner: Owner,
/// Upgrade state
pub type_: Upgrade,
/// Number of links on the map
pub links: u8,
/// Active link states
pub link_states: Vec<Arc<Link>>,
/// Input buffer
#[serde(skip)]
pub buffer: Vec<Packet>,
}
pub type LinkId = usize;
/// A one-to-one link between two nodes
#[derive(Serialize, Deserialize)]
pub struct Link {
/// This link ID
id: LinkId,
/// Node 1
a: NodeId,
/// Node 2
b: NodeId,
/// The step length
length: usize,
/// Packets present on this link
#[serde(skip)]
pp: Vec<(Packet, AtomicU32)>,
/// Actual Rx, Tx pair
#[serde(skip)]
io: Io,
}
pub type PacketId = usize;
/// A packet going across the network
pub struct Packet {
/// The packet ID
id: PacketId,
/// Declare this packet to be removed
dead: AtomicBool,
/// Each packet is owned by a player
owner: Arc<Player>,
/// What type of packet this is
data_: PacketType,
}
pub type PlayerId = usize;
/// A player who's having fun
#[derive(Serialize, Deserialize)]
pub struct Player {
/// A unique player ID (per match)
pub id: PlayerId,
/// The player name
pub name: String,
/// Player color
pub color: Color,
/// The player's money
pub money: AtomicU16,
}
/// Optionally, players can create teams
#[derive(Serialize, Deserialize)]
pub struct Team {
/// Name of the team
name: String,
/// Unified color of the team
color: Color,
/// All team members by their ID
roster: Vec<u16>,
}
/// An RGB color without alpha
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Color(u8, u8, u8);
impl Color {
pub fn black() -> Self {
Self(50, 50, 50)
}
pub fn red() -> Self {
Self(250, 50, 50)
}
pub fn green() -> Self {
Self(100, 250, 100)
}
pub fn blue() -> Self {
Self(100, 100, 250)
}
pub fn teal() -> Self {
Self(150, 250, 250)
}
pub fn purple() -> Self {
Self(150, 100, 250)
}
pub fn orange() -> Self {
Self(250, 200, 100)
}
pub fn yellow() -> Self {
Self(250, 250, 100)
}
pub fn white() -> Self {
Self(225, 225, 225)
}
}
pub trait ColorPalette {
/// Create a new color palette
fn palette() -> Self;
/// Get a palette without a certain colour
fn without(&mut self, b: &Color);
/// Mix a color back into the available palette
fn remix(&mut self, new: Color);
}
impl ColorPalette for Vec<Color> {
fn palette() -> Self {
let mut rng = thread_rng();
let mut pal = vec![
Color::black(),
Color::red(),
Color::green(),
Color::blue(),
Color::teal(),
Color::purple(),
Color::orange(),
Color::yellow(),
Color::white(),
];
pal.shuffle(&mut rng);
pal
}
/// Drop a colour from the palette
fn without(&mut self, b: &Color) {
if let Some(pos) = self.into_iter().rposition(|a| a == b) {
self.remove(pos);
}
}
fn remix(&mut self, new: Color) {
let mut rng = thread_rng();
self.push(new);
self.shuffle(&mut rng);
}
}
/// Describes ownership state
#[derive(Serialize, Deserialize)]
pub enum Owner {
/// Nobody owns this
Neutral,
/// A player owns this
Player(Player),
}
/// Encodes upgrade level without numbers
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum Level {
/// 1
One,
/// 2
Two,
/// 3 (wow)
Three,
}
/// Describes upgrade state
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum Upgrade {
/// A basic node
Base,
/// Battle (attack/defence) nodes
Guard(Level),
/// These nodes make money
Compute(Level),
/// Good at packet switching
Relay(Level),
}
/// Possible types of packets
pub enum PacketType {
/// A keepalive packet
///
/// These are sent by all nodes if their neighbours are either
/// friendly or neutral, and used to keep the network alive.
/// Sending them costs nothing, and when they are received, they
/// yield a small amount of funds, and restoring health of a node.
Ping,
/// A non exploit capture
///
/// This is a packet that can be sent out by any node (except a
/// switch) to claim a neutral node on the network. The path to
/// the node needs to consist only of friendlies, and if an enemy
/// node processes this type, nothing happens.
Capture,
/// A compute packet
///
/// The first value is the target compute value, which each
/// compute node adds on to. The second value is the current. If
/// a compute packet passes through a compromised or enemy node,
/// it might subtract from the second value, before palling it on.
Compute {
max: u16,
curr: AtomicU16,
step: u16,
},
/// A special wrapper packet generated by guards
///
/// Sometimes, when a hostily attack packet encounters a guard, it
/// manages to capture the attack, and forwards it to a random
/// compute node. If the node manages to handle the packet,
/// without it getting dropped in the meantime), it yields a
/// specified reward, like a computation would.
Payload { inner: Box<PacketType>, reward: u16 },
/// A reset attack packet
///
/// When encountering a hostile node, it will make that node drop
/// all packets in it's buffers.
Reset,
/// Cross-node-scripting attack
///
/// Decreases the strength of a node, also having a small chance
/// of spawning a new packet into a random output buffer. When
/// applied to a neutral node, it makes capturing nodes go faster.
CNS,
/// Node-in-the-middle attack
///
/// Infect the routing behaviour of a node to route all traffic to
/// a specified enemy node instead
Nitm,
/// Virus infection attack
///
/// Infects a node to capture it's earnings, both active and
/// passive, for a short time, without taking on it's costs.
Virus,
/// A total control exploit
///
/// This is very hard to do, and a node will basically always
/// resist it, but if successful, transforms the node into a guard
/// node and yields control to the attackinng player.
TakeOver,
}
|